home *** CD-ROM | disk | FTP | other *** search
/ SPACE 1 / SPACE - Library 1 - Volume 1.iso / program / 386 / arpbook6 / chap_6.doc
Text File  |  1989-03-30  |  91KB  |  2,172 lines

  1.    Atari ST Machine Specific Programming In Assembly
  2.    
  3. Chapter 6: Selections Based On Speed
  4.  
  5.  
  6. Reducing Program Loading Time
  7.  
  8.      As I was concluding chapter 3, I intended to indicate 
  9. that, in many ways, this chapter is an extension of that 
  10. one.  For example, the first thing that I shall do here is 
  11. to resume the discussion of assembly modes.  As I stated 
  12. there, the first step involved in rapid execution is rapid 
  13. loading.  A program cannot even begin run until it has been 
  14. moved from disk storage to ram.
  15.      For programs which simply load, execute and terminate, 
  16. without spending much time in ram, the time required to load 
  17. the program may be a significant portion of the total time 
  18. involved in program execution.  Many of the programs that 
  19. are executed from an AUTO folder during boot are of this 
  20. type.  It seems reasonable, therefore, that discussions 
  21. which involve the reduction of loading time precede 
  22. discussions involving the reduction of execution time.
  23.      As I mentioned in program SPEED_1's documentation, the 
  24. time required to load a particular program from disk to ram 
  25. depends on the assembly mode which produced the code; the 
  26. type of drive from which it is loaded, hard disk or floppy; 
  27. the method used to format the disk, such as ST normal, 
  28. Double Click's DC Formatter or David Small and Dan Moore's 
  29. Mega-Twister; the position of the file on the disk, which 
  30. could include fragmentation; and, if the program is spawned 
  31. by another, the distance between parent and child, which 
  32. could include intermedia displacement.
  33.  
  34. The Effects of Assembly Modes
  35.   
  36.      Programs 23 and 24 have been prepared for use in 
  37. comparing the load and execution times of a program 
  38. assembled in three assembly modes.  Program 23 should be 
  39. assembled in the Relocatable mode to produce PRG_2CC.TOS.  
  40. Program 24 should be assembled in AssemPro's PC-relative 
  41. mode to produce PRG_2CP.TOS, then the name of the source 
  42. should be changed to PRG_2CR, while it is still in the 
  43. editor, so that it can be assembled in the Relocatable mode 
  44. to produce PRG_2CR.TOS.
  45.      I obtained the load and execute times for the three TOS 
  46. programs using the following method to isolate the 
  47. experiment from the intermedia displacement variables 
  48. discussed above.  Execution results follow the appropriate 
  49. listings.
  50.   
  51.      1. Copy SPEEDTST.TTP to a blank hard disk partition or 
  52.         floppy disk.
  53.  
  54.      2, Copy PRG_2CC.TOS to the same hard disk partition or 
  55.         floppy disk.
  56.  
  57.      3. Execute SPEEDTST.TTP and type PRG_2CC.TOS on the 
  58.         command line.
  59.  
  60.      4. Copy PRG_2CC.DAT to a safe place.
  61.   
  62.      5. Remove PRG_2CC.TOS and PRG_2CC.DAT from the 
  63.         partition or floppy.
  64.  
  65.      6. Repeat steps 2 through 5 for PRG_2CP.TOS and 
  66.         PRG_2CR.TOS.
  67.  
  68. Program 23. Source file for PRG_2CC.TOS
  69.  
  70.  ; Program Name: PRG_2CC.S
  71.  ;      Version: 1.002
  72.  
  73.  ; Assembly Instructions:
  74.  
  75.  ;     Assemble in Relocatable mode and save with a TOS extension.
  76.  
  77.  ; Execution Instructions:
  78.  
  79.  ;     Execute program SPEEDTST.TTP and type PRG_2CC.TOS on the input
  80.  ; parameter line.  SPEEDTST.TTP will produce a data file named PRG_2CC.DAT
  81.  ; on disk.  You will be able to compare the data for this program to that
  82.  ; produced for programs PRG_2CP.TOS and PRG_2CR.TOS.
  83.  
  84.  ; Program Function:
  85.  
  86.  ;     Statements within a nested loop structure are executed 50,000 times
  87.  ; so that the load and execution time of this program can be compared with
  88.  ; similar programs assembled in the PC-relative and Relocatable modes.
  89.     
  90. store_after_load_time:
  91.  trap       #3                  ; Returns value of system clock in D0. 
  92.  lea        after_load_time(pc), a0   
  93.  move.w     d0, (a0)
  94.  
  95.  move.w     #9, d1              ; Initialize outer loop counter.
  96. outer_loop:                     ; Loop ten times.
  97.  move.w     #49999, d0          ; Initialize inner loop counter.
  98. inner_loop:                     ; Loop 50,000 times.
  99.  move.l     #label, a0          ; Can't use (pc) here.
  100.  lea        label(pc), a0   
  101.  move.l     label(pc), a0
  102.  move.l     #label, -(sp)       ; Can't use (pc) here.
  103.  pea        label(pc)
  104.  move.l     label(pc), -(sp)
  105.  lea        $C(sp), sp          ; Reposition stack pointer to top of stack.
  106.  dbra       d0, inner_loop      ; Loop back until D0 = -1.
  107.  dbra       d1, outer_loop      ; Loop back until D1 = -1.
  108.  
  109. terminate:
  110.  move.w    after_load_time(pc), -(sp) ; Pass after load time to SPEEDTST.TTP.
  111.  move.w     #$4C, -(sp)               ; Function = p_term = GEMDOS $4C.
  112.  trap       #1        
  113.  
  114.  data
  115.  
  116.  ; NOTE: Below, the variable "label" is supposed to be a pointer to the
  117.  ;       variable "after_load_time".  If this program is assembled in
  118.  ;       Relocatable mode, the "run time" address of "after_load_time" will be
  119.  ;       stored in the 4 bytes declared at "label" when the program is loaded
  120.  ;       from disk to ram.
  121.  
  122.  ;       But, if the program is assembled in PC-relative mode, the "run time"
  123.  ;       address will not be stored there; instead, the "assembly time" address
  124.  ;       will be stored in the 4 bytes.  That is undesirable.
  125.  
  126. label:           dc.l after_load_time     ; This works for COMBO assembly.
  127.  bss
  128. after_load_time: ds.w 1
  129.  end
  130.  
  131.  
  132. SPEEDTST.TTP Execution Results
  133. for PRG_2CC.TOS, loaded from drive: G
  134.  
  135.   Load time:       1995 milliseconds
  136.   Execution time:  7485 milliseconds
  137.   
  138.   
  139. Program 24. Source file for PRG_2CP.TOS and 
  140. PRG_2CR.TOS.
  141.  
  142.  ; Program Name: PRG_2CP.S
  143.  ;      Version: 1.002
  144.  
  145.  ; Assembly Instructions:
  146.  
  147.  ;     Assemble in PC-relative mode and save with a TOS extension, then
  148.  ; change the name to PRG_2CR, assemble in Relocatable mode and save as
  149.  ; PRG_2CR.TOS.  This will allow you to prepare two object code files from
  150.  ; the same source file.
  151.  
  152.  ; Execution Instructions:
  153.  
  154.  ;     Execute program SPEEDTST.TTP and type PRG_2CP.TOS on the input
  155.  ; parameter line.  SPEEDTST.TTP will produce a data file named PRG_2CP.DAT
  156.  ; on disk.  You will be able to compare the data for this program to that
  157.  ; produced for programs PRG_2CC.TOS and PRG_2CR.TOS.
  158.  
  159.  ;     Do the same with PRG_2CR.TOS to produce a data file named PRG_2CR.DAT.
  160.  ; You can view the DAT files with an editor, from the desktop using the Show
  161.  ; function or by printing them.
  162.  
  163.  ; Program Function:
  164.  
  165.  ;     Statements within a nested loop structure are executed 50,000 times
  166.  ; so that the load and execution time of this program can be compared with
  167.  ; similar programs assembled in the Relocatable and Combo modes.
  168.  
  169. store_after_load_time:
  170.  trap       #3                  ; Returns value of system clock in D0.      
  171.  lea        after_load_time, a0 
  172.  move.w     d0, (a0)            ; Store time in variable "after_load_time".
  173.  move.w     #9, d1              ; Initialize outer loop counter.
  174. outer_loop:                     ; Loop ten times.
  175.  move.w     #49999, d0          ; Initialize inner loop counter.
  176. inner_loop:                     ; Loop 50,000 times.
  177.  move.l     #label, a0
  178.  lea        label, a0   
  179.  move.l     label, a0
  180.  move.l     #label, -(sp)
  181.  pea        label
  182.  move.l     label, -(sp)
  183.  lea        $C(sp), sp          ; Reposition stack pointer to top of stack.
  184.  dbra       d0, inner_loop      ; Loop back until D0 = -1.
  185.  dbra       d1, outer_loop      ; Loop back until D1 = -1.
  186.  
  187. terminate:
  188.  move.w     after_load_time, -(sp) ; Pass after load time to SPEEDTST.TTP.
  189.  move.w     #$4C, -(sp)            ; Function = p_term = GEMDOS $4C.
  190.  trap       #1                  
  191.  
  192.  data
  193.  
  194.  ; NOTE: Below, the variable "label" is supposed to be a pointer to the
  195.  ;       variable "after_load_time".  If this program is assembled in
  196.  ;       Relocatable mode, the "run time" address of "after_load_time" will be
  197.  ;       stored in the 4 bytes declared at "label" when the program is loaded
  198.  ;       from disk to ram.
  199.  
  200.  ;       But, if the program is assembled in PC-relative mode, the "run time"
  201.  ;       address will not be stored there; instead, the "assembly time" address
  202.  ;       will be stored in the 4 bytes.  That is undesirable.
  203.  
  204. label:     dc.l after_load_time ; This does not give desired response for
  205.  bss                            ; PC-relative assembly, it works for
  206. after_load_time: ds.w 1         ; Relocatable assembly.
  207.  end
  208.  
  209.  
  210. SPEEDTST.TTP Execution Results
  211. for PRG_2CP.TOS, loaded from drive: G
  212.  
  213.   Load time:         35 milliseconds
  214.   Execution time:  7475 milliseconds
  215.  
  216.  
  217. SPEEDTST.TTP Execution Results
  218. for PRG_2CR.TOS, loaded from drive: G
  219.  
  220.   Load time:       1995 milliseconds
  221.   Execution time:  8510 milliseconds
  222.  
  223.  
  224.      A comparison of load times is dramatic.  When the 
  225. program is assembled in AssemPro's PC-relative mode, the 
  226. time required to load is 1960 msec (about 2 seconds) less 
  227. than it is when the program is assembled in Relocatable 
  228. mode.  And, although the use of pc-relative addressing in a 
  229. program that is assembled in Relocatable mode does not 
  230. reduce load time, no less dramatic is its effect on 
  231. execution time.  Because for even so short a program as 
  232. this, it reduces the execution time from 8510 msec to 7485 
  233. msec, effecting a 1 second savings.
  234.  
  235. Why PC-relative Programs Load Faster
  236.  
  237.      As I mentioned in chapter 2, part of the extra load 
  238. time (or that which I have chosen to designate as load time, 
  239. but which could as easily be called start-up time) required 
  240. by programs assembled in the Relocatable mode is due to the 
  241. task of program relocation itself.  I have since received 
  242. information from a reader which further clarifies the 
  243. comparison between the two assembly modes.  This reader is 
  244. well known for his hard disk backup utility, TURTLE.PRG.  I 
  245. speak, of course, of George Woodside.
  246.      I now paraphrase the information that Mr. Woodside was 
  247. kind enough to share with me.  It seems that when a 
  248. Relocatable program is executed the operating system clears 
  249. all unoccupied ram before the onset of execution.  In 
  250. addition, Mr. Woodside went on to explain that PC-relative 
  251. LSR programs which are executed from the AUTO folder can't 
  252. be deleted because the operating system does not close those 
  253. files while they are still ram resident.  I have mentioned 
  254. this phenomenon elsewhere, but I have not known the reason 
  255. until now.
  256.      I had planned to write a few programs that would 
  257. graphically display the information presented by Mr. 
  258. Woodside, but something has occurred which probably renders 
  259. such explorations irrelevant from my position.  Since 
  260. receiving his letter I have been forced to purchase a MEGA 2 
  261. because I needed the additional memory.  And, while the 
  262. differences in loading and execution times are still 
  263. noticeable, they are not as dramatic as they were with the 
  264. 1040ST.  Therefore, I simply rest the matter by saying that 
  265. there is a difference and that difference is reported as I 
  266. experienced it.
  267.  
  268. The Effects of Floppy Disk Formatting Techniques
  269.   
  270.      The first thing I'm going to do in this section is to 
  271. refer you to a couple of START magazine articles written by 
  272. Dave Small and Dan Moore: Hard Disk Warfare, Spring, 1987 
  273. and Let's Twist Again, Summer, 1988.  Those articles will 
  274. help you to understand why this section is included in the 
  275. book.  Briefly, there is more than one way to format floppy 
  276. disks for the ST.  I have designed two experiments which 
  277. permit you to compare the effects of two non-standard 
  278. methods to that of the standard ST method.
  279.      I refer to the formatted disk you obtain when you 
  280. format from the desktop by selecting the Format option under 
  281. the File menu as standard ST.  I refer to the formatted disk 
  282. you obtain by any other method as non-standard.  The non-
  283. standard formatting methods used in the experiment are Mega 
  284. Twister, included on START magazine's Summer, 1988 disk and 
  285. DC Formatter Version 2.2, included on ST Informer's PDM 188 
  286. disk.
  287.      The most extensive reference to DC Formatter that I 
  288. could find occurs in the July/August 1988 issue of RESET 
  289. magazine, on page 11, within the Public Beat column.  If 
  290. this article is not readily available to you, I suggest that 
  291. you be not concerned about it.  While the author's 
  292. enthusiasm for DC Formatter is undoubtedly genuine, and, 
  293. although I have found DC Formatter version 2.2 to format 
  294. faster than Mega Twister (1 minute 7 seconds versus 1 minute 
  295. 45 seconds), and, although the data in figures 6.1 and 6.2 
  296. seem to confirm that DC Formatter may indeed write to 
  297. floppies slightly faster than Mega Twister, the data does 
  298. not confirm the author's apparent conclusion that DC 
  299. Formatter is tremendously faster than Mega Twister.
  300.      A new version of DC Formatter, Version 3.0, is 
  301. discussed in the December, 1988 issue of ST Informer.  
  302. Before deciding to use this method of formatting disks, I 
  303. suggest that you read the information on page 33 of that 
  304. issue, under the heading DCFRMT3.ARC.  There you will find a 
  305. statement concerning the incompatibility of the earlier 
  306. version with GDOS.  Furthermore, I suggest that you use any 
  307. non-standard method of formatting disks with all of the 
  308. caution the word non-standard connotes.
  309.      Program 25 has been designed to permit a comparison of 
  310. the load, write and read times associated with each disk 
  311. formatting method.  The steps used to isolate the program on 
  312. the appropriate disks are discussed within the program's 
  313. documentation.  Program 25 invokes trap calls to custom trap 
  314. #10, which is installed by executing TRAP_10.PRG.  The 
  315. source listing for TRAP_10.PRG follows the listing for 
  316. program 25.
  317.   
  318. Program 25. This program is used to compare the effects 
  319. of three floppy disk formatting techniques.
  320.  
  321.  ; Program Name: PRG_2DP.S
  322.  ; Version 1.003
  323.  
  324.  ; Assembly Instructions:
  325.  
  326.  ;    Assemble in PC-relative mode and save with a TOS extension.
  327.  
  328.  ; Execution Note:
  329.  
  330.  ;    This program invokes custom traps which must be installed by
  331.  ; TRAPS.PRG and TRAP_10.PRG prior to its execution.  Execute this program
  332.  ; by typing its name on SPEEDTST.TTP's command line.
  333.  
  334.  ;    But before executing this program, prepare three floppy disks.  The
  335.  ; first should be formatted from the desktop, using the ST formatting
  336.  ; algorithm.  The second should be formatted with a version of DCFORMAT,
  337.  ; available from ST Informer, 909 NW Starlite Place, Grants Pass, OR 97526,
  338.  ; (503)476-0071 on disk PDM 188 or PDM 1288.  The price of each disk from
  339.  ; ST Informer is $6.00 for non-subscribers.  You can subscribe for $18.00
  340.  ; per year and get a free disk coupon.  I highly recommend a subscription.
  341.  
  342.  ;    The third disk should be formatted with Dave Small and Dan Moore's
  343.  ; Twister program available on START magazine's Summer, 1988 issue.  You
  344.  ; can find an order form in any issue of START or phone (800)234-7001.
  345.  
  346.  ;    Copy PRG_2DP.TOS to each blank disk.  Copy SPEEDTST.TTP to each disk.
  347.  ; Execute SPEEDTST.TTP on each disk in turn, typing PRG_2DP.TOS on its
  348.  ; command line.  Compare the contents of the WRITE_1 and WRITE_2 files of
  349.  ; each disk to verify that they are identical.  Compare the PRG_2DP.DAT file
  350.  ; on each disk to the others. 
  351.  
  352.  ; Program Function:
  353.  
  354.  ;    This program writes data to WRITE_1.DAT, reads WRITE_1.DAT, then
  355.  ; writes what it has read to WRITE_2.DAT to confirm that it has correctly
  356.  ; written and read the data declared within the data section of the program.
  357.  
  358.  ;    SPEEDTST.TTP will report the load and total execution time for this
  359.  ; program.
  360.  
  361.  ;    Within the program, the time to write the data to WRITE_1.DAT will be
  362.  ; calculated and reported; and the time to read the data from WRITE_1.DAT
  363.  ; will also be calculated and reported; finally, the time to write the data
  364.  ; to WRITE_2.DAT will be calculated and reported.
  365.  
  366.  ;    This program is to be used to compare the write to and read from times
  367.  ; involving three floppy disks, each of which has been formatted with a
  368.  ; different formatting algorithm.
  369.  
  370. fetch_load_time:              
  371.  trap       #3                  ; Returns value of system clock in D0.
  372. release_excess_memory:          ; Also stores after-load time in TRAPS bss.
  373.  lea        program_end, a0     ; Put "end of program" address in A0.
  374.  movea.l    4(a7), a1           ; Put "basepage" address in A1.
  375.  trap       #6                  ; Calculate program size and release memory.
  376.  lea        stack, a7
  377.  
  378. print_heading:
  379.  lea        heading, a0
  380.  bsr        print_string
  381.  
  382. fetch_write_start_time:
  383.  trap       #3
  384.  lea        write_start_time, a0
  385.  move.w     d0, (a0)
  386. create_file_1:
  387.  move.w     #0, -(sp)           ; File attribute = read/write.
  388.  pea        file_1_name         ; For WRITE_1.DAT.
  389.  move.w     #$3C, -(sp)         ; Function = f_create = GEMDOS $3C.
  390.  trap       #1                  ; File handle is returned in D0.
  391.  addq.l     #8, sp
  392.  lea        file_1_handle, a0   ; Store returned file handle.
  393.  move.w     d0, (a0)
  394.  
  395. write_to_file_1:
  396.  pea        string               ; Push address of buffer.
  397.  move.l     #451, -(sp)          ; Number of bytes to write.
  398.  move.w     d0, -(sp)            ; File handle to be written to.
  399.  move.w     #$40, -(sp)          ; GEMDOS function =  write.
  400.  trap       #1
  401.  lea        $C(sp), sp           ; Reposition stack pointer to top of stack.
  402. close_file_1:                    
  403.  move.w     file_1_handle, -(sp) 
  404.  move.w     #$3E, -(sp)          ; Function = GEMDOS $3E = f_close.
  405.  trap       #1
  406.  addq.l     #4, sp
  407. get_end_time:
  408.  trap       #3
  409.  sub.w      write_start_time, d0 ; Subtract start time from end time.
  410.  ext.l      d0                   ; Extend to 32 bits
  411.  trap       #10                  ; Convert to milliseconds and print.
  412.  
  413. set_dta:
  414.  pea        dta                  ; dta = address of 44 byte buffer.
  415.  move.w     #$1A, -(sp)          ; GEMDOS function = set dta.
  416.  trap       #1
  417.  addq.l     #6, sp
  418. print_read_time_label:
  419.  lea        read_msg, a0
  420.  bsr        print_string
  421. fetch_read_start_time:
  422.  trap       #3
  423.  lea        read_start_time, a0
  424.  move.w     d0, (a0)
  425.  
  426.  ; NOTE: Reading is so fast, must loop to accumulate enough time to measure.
  427.  
  428.  move.w     #99, d3              ; Set up counter for 100 loops.
  429. search_for_file: 
  430.  move.w     #0, -(sp)            ; Attribute = normal access.
  431.  pea        file_1_name          ; Name of file to search for.
  432.  move.w     #$4E, -(sp)          ; GEMDOS function = search first.
  433.  trap       #1
  434.  addq.l     #8, sp
  435.  tst        d0
  436.  bne.s      not_found
  437.  
  438. read_WRITE_1_DAT:
  439.  lea        dta, a0
  440.  pea        buffer
  441.  move.l     $1A(a0), -(sp)       ; Number of bytes to read.
  442.  move.w     file_1_handle, -(sp) ; File to read.
  443.  move.w     #$3F, -(sp)          ; GEMDOS function = read.
  444.  trap       #1
  445.  lea        $C(sp), sp           ; Reposition stack pointer.
  446.  dbra       d3, search_for_file
  447. _get_end_time:
  448.  trap       #3
  449.  sub.w      read_start_time, d0  ; Subtract start time from end time.
  450.  ext.l      d0                   ; Extend to 32 bits
  451.  trap       #10                  ; Convert to milliseconds and print.
  452.  
  453. print_write_time_label:
  454.  lea        write_msg, a0
  455.  bsr        print_string
  456. _fetch_write_start_time:
  457.  trap       #3
  458.  lea        write_start_time, a0
  459.  move.w     d0, (a0)
  460. create_file_2:
  461.  move.w     #0, -(sp)            ; File attribute = read/write.
  462.  pea        file_2_name          ; For WRITE_2.DAT.
  463.  move.w     #$3C, -(sp)          ; Function = f_create = GEMDOS $3C.
  464.  trap       #1                   ; File handle is returned in D0.
  465.  addq.l     #8, sp
  466.  lea        file_2_handle, a0    ; Store returned file handle.
  467.  move.w     d0, (a0)
  468. write_to_file_2:
  469.  lea        dta, a0
  470.  pea        string               ; Push address of buffer.
  471.  move.l     $1A(a0), -(sp)       ; Number of bytes to write.
  472.  move.w     d0, -(sp)            ; File handle to be written to.
  473.  move.w     #$40, -(sp)          ; GEMDOS function =  write.
  474.  trap       #1
  475.  lea        $C(sp), sp           ; Reposition stack pointer to top of stack.
  476. close_file_2:        
  477.  move.w     file_2_handle, -(sp) 
  478.  move.w     #$3E, -(sp)          ; Function = GEMDOS $3E = f_close.
  479.  trap       #1
  480.  addq.l     #4, sp
  481. _get__end_time:
  482.  trap       #3
  483.  sub.w      write_start_time, d0 ; Subtract start time from end time.
  484.  ext.l      d0                   ; Extend to 32 bits
  485.  trap       #10                  ; Convert to milliseconds and print.
  486.  
  487. not_found:
  488.  trap       #8                   ; Terminate.
  489.  
  490. print_string:
  491.  pea        (a0)
  492.  move.w     #9, -(sp)
  493.  trap       #1
  494.  addq.l     #6, sp
  495.  rts
  496.  
  497.  data
  498. file_1_name: dc.b 'WRITE_1.DAT',0     
  499. file_2_name: dc.b 'WRITE_2.DAT',0
  500. heading:     dc.b 'PRG_2DP.TOS Execution Results',$D,$A,$D,$A
  501.              dc.b '  Time to create, write and close WRITE_1.DAT:   ',0
  502. read_msg     dc.b '  Time to read WRITE_1.DAT into buffer 10 times: ',0 
  503. write_msg:   dc.b '  Time to create, write and close WRITE_2.DAT:   ',0 
  504. string:      dc.b '  This paragraph will be written to a disk file named '
  505.              dc.b 'WRITE_1.DAT.  The time ',$D,$A
  506.              dc.b '  required to write the paragraph will be reported in '
  507.              dc.b 'file PRG_2DP.DAT.',$D,$A
  508.              dc.b '  Then the contents of WRITE_1.DAT will be read into a '
  509.              dc.b 'buffer.  The time ',$D,$A
  510.              dc.b '  required to read the contents of the file will be '
  511.              dc.b 'reported in file ',$D,$A
  512.              dc.b '  PRG_2DP.DAT.  Finally, the contents of the buffer '
  513.              dc.b 'will be written to ',$D,$A
  514.              dc.b '  WRITE_2.DAT so that what has been read can be compared '
  515.              dc.b 'to what was written.',$D,$A,$1A
  516.  
  517.  ; NOTE: The ASCII code for ^Z (control Z) is normally used to mark the end
  518.  ;       of a file so that a program reading the file may look for that mark.
  519.  
  520.  bss
  521.  align
  522. file_1_handle:    ds.w   1
  523. file_2_handle:    ds.w   1
  524. write_start_time: ds.w   1
  525. read_start_time:  ds.w   1
  526. dta:              ds.b  44
  527. buffer:           ds.b 452
  528.                   ds.l  96
  529. stack:            ds.l   0
  530. program_end:      ds.l   0
  531.  end
  532.   
  533.  
  534. Program 26. The custom trap #10 algorithm.
  535.  
  536.  ; Program Name: TRAP_10.S
  537.  ; Version 1.001
  538.  
  539.  ; Assembly Instructions:
  540.  
  541.  ;    Assemble in PC-relative mode and save with a PRG extension.
  542.  
  543.  ; Program Function:
  544.  
  545.  ;    This is a LSR program that establishes a user defined trap.  It may be
  546.  ; executed from the desktop, but you may prefer to copy it to the AUTO
  547.  ; folder of your boot partition or floppy disk so that it will execute
  548.  ; automatically during boot.
  549.  
  550.  ;    Trap #10 is used by programs which time an interval.  This trap
  551.  ; converts the interval passed in register D0 to milliseconds, then it
  552.  ; prints the ASCII decimal value of that interval in milliseconds.
  553.  
  554.  ;    This program invokes a custom trap that is established by TRAPS.PRG,
  555.  ; therefore, that program must be executed before trap #10 is invoked by a
  556.  ; program.
  557.  
  558. program_start:                  ; Calculate program size and retain result.
  559.  lea        program_end, a3     ; Fetch program end address.
  560.  suba.l     4(a7), a3           ; Subtract basepage address.
  561.  
  562. enter_supervisor_mode:
  563.  move.l     #0, -(sp)           ; The zero turns on supervisor mode.
  564.  move.w     #$20, -(sp)         ; Function = super = GEMDOS $20.
  565.  trap       #1                  ; Go to supervisor mode.
  566.  addq.l     #6, sp              ; Supervisor stack pointer (SSP) returned in D0.
  567.  movea.l    d0, a5              ; Save SSP in scratch register.
  568.  
  569. install_trap_10_routine:        ; Note: pointer = vector = pointer.
  570.  lea        $A8, a0             ; Fetch trap #10 pointer address.
  571.  lea        trap_10_routine, a1 ; Fetch address of trap #10 routine.
  572.  move.l     a1, (a0)            ; Store trap address at pointer address.
  573.  
  574. enter_user_mode:
  575.  pea        (a5)                ; Restore supervisor stack pointer.
  576.  move.w     #$20, -(sp)         ; Function = super = GEMDOS $20.
  577.  trap       #1                  ; Go to user mode.
  578.  addq.l     #6, sp              ; Reset stack pointer to top of stack. 
  579.  
  580. relinquish_processor_control:   ; Maintain memory residency.
  581.  move.w    #0, -(sp)            ; See page 121 of Internals book.
  582.  move.l    a3, -(sp)            ; Program size.
  583.  move.w    #$31, -(sp)          ; Function = ptermres = GEMDOS $31.
  584.  trap      #1
  585.  
  586. trap_10_routine:
  587.  
  588.  ; Expects a binary value in D0 which represents a measured interval.  This
  589.  ; algorithm converts the value in D0 to milliseconds (msec) then prints the
  590.  ; value in decimal msec.
  591.  
  592. preserve_value_in_d3:
  593.  move.l     d3, -(sp)
  594. convert_time_to_msec:
  595.  move.l     d0, d3              ; Save copy in D0 to add.
  596.  asl.l      #2, d3              ; Shift to multiply by 4.
  597.  add.l      d0, d3              ; To complete multiplication by 5.
  598.  
  599. print_time:
  600.  cmpi.l     #999, d3            ; If time is less than 1000, then
  601.  bgt        no_space            ; print a leading blank space for output
  602.  lea        space, a0           ; alignment.
  603.  bsr        print_string
  604.  cmpi.l     #99, d3             ; If time is less than 100, then
  605.  bgt        no_space            ; print another leading blank space.
  606.  lea        space, a0
  607.  bsr        print_string
  608. no_space:
  609.  move.l     d3, d1              ; Move to D1 so can convert to ASCII decimal
  610.  trap       #4                  ; Returns address of decimal string in A0.
  611.  bsr.s      print_string
  612.  lea        units_label, a0
  613.  bsr.s      print_string
  614.  move.l     (sp)+, d3           ; Restore contents of D3. 
  615.  rte
  616.  
  617.  ;
  618.  ; Subroutine
  619.  ;
  620.  
  621. print_string:                   ; Expects address of string to be in A0.
  622.  pea        (a0)                ; Push address of string onto stack.
  623.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  624.  trap       #1                  ; GEMDOS call
  625.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  626.  rts
  627.  
  628.  data
  629. space:            dc.b   " ",0
  630. units_label:      dc.b   "  milliseconds", $D,$A,0
  631.  bss
  632.  align                       ; Align storage on a word boundary.
  633. program_end:      ds.l   0
  634.  end
  635.  
  636.  
  637.      Figure 6.1 shows the execution results of program 25 
  638. with the contents of memory location $444 unaltered (See 
  639. page 252 of the Internals book and Dave's Write-With-Verify 
  640. Lecture, page 88 of START's Spring, 1987 issue.).  As you 
  641. will see, it makes sense to perform the experiment twice, 
  642. once with the ST standard value in this memory location and 
  643. once with the value zero  stored there.  And since that 
  644. subject has come up, this is a convenient spot at which to 
  645. introduce a program that I use to configure certain ST 
  646. system variables during boot.  Refer to section 3.7 The ST 
  647. System Variables, pages 250-257 of the Internals book.
  648.  
  649. Program 27. A program used to configure system variables 
  650. during boot.  I do not guarantee that this program will work 
  651. with all ST systems.
  652.  
  653.  ; Program Name: CONFIG.S
  654.  
  655.  ; Assembly Instructions:
  656.  
  657.  ;     Assemble in PC-relative mode and save with a PRG extension.  Move
  658.  ; CONFIG.PRG to the AUTO folder of the boot disk.
  659.  
  660.  ; Program Purpose:
  661.  
  662.  ;     Configures system variables.
  663.  
  664. mainline:
  665.  lea        stack, a7           ; Point A7 to this program's stack.
  666.  
  667. enter_supervisor_mode:
  668.  move.l     #0, -(sp)           ; The zero turns on supervisor mode.
  669.  move.w     #$20, -(sp)         ; Function = super = GEMDOS $20.
  670.  trap       #1                  ; Supervisor stack pointer returned in D0.
  671.  addq.l     #6, sp
  672.  
  673.  ; Algorithm 1:
  674.  
  675.  ; Turns off keyclick sound.  Refer to page 254 of the Internals book.  The
  676.  ; system variable at address $484 is a byte length variable.  The bits of
  677.  ; this variable have the meanings as indicated in the Internals book.  The
  678.  ; bit of interest is #0.  When this bit is a one, the computer emits a
  679.  ; click each time a key is pressed.  When the bit is a zero, these clicks
  680.  ; are not emitted.  A zero is placed in this bit by replacing the content
  681.  ; of the byte at $484 (which is 7 before the replacement, if key click is
  682.  ; enabled) with $6.
  683.  
  684. disable_key_click:
  685.  move.b     #6, $484            ; Refer to page 254 of the Internals book.
  686.  
  687.  ; Algorithm 2:
  688.  
  689.  ;     Performs the printer installation that is accomplished by CONTROL.ACC.
  690.  ; The printer configuration table consists of one word, stored at $E4A.
  691.  ; The bits of this word have the following meanings:
  692.  
  693.  ;            BIT  MEANING IF ZERO  MEANING IF ONE
  694.  ;            ---  ---------------  --------------
  695.  ;             0   Dot matrix       Daisy printer
  696.  ;             1   Black/white      Color printer
  697.  ;             2   1280 dots/line   960 dots/line
  698.  ;             3   Draft mode       Final mode
  699.  ;             4   Printer port     Modem port
  700.  ;             5   Continuous feed  Single sheet
  701.  
  702.  ; Bits 6 through 15 are not used.
  703.  
  704. install_printer:
  705.  move.w     #$4, $E4A
  706.  
  707.  ; Algorithm 3:
  708.  
  709.  ; Turns off disk write verify.
  710.  
  711. disable_write_verify:
  712.  move.w     #0, $444            ; Refer to page 252 of the Internals book.
  713.  
  714. return_to_user_mode:
  715.  move.l     d0, -(sp)           ; Restore "before call" SSP.
  716.  move.w     #$20, -(sp)         ; Function = super = GEMDOS $20.
  717.  trap       #1                 
  718.  addq.l     #6, sp
  719.  
  720. terminate:
  721.  move.w     #0, -(sp)           ; Function = p_term_old = GEMDOS $0.
  722.  trap       #1                  ; GEMDOS call.
  723.  
  724.              ds.l    24         ; Stack.
  725. stack:       ds.l     1         ; Address of stack.
  726. program_end: ds.l     0
  727.  end
  728.  
  729.  
  730. Figure 6.1. Program 25's execution results with write-verify 
  731. active.
  732.  
  733. PRG_2DP.TOS Execution Results: Hard disk drive.
  734.  
  735.   Time to create, write and close WRITE_1.DAT:   160  milliseconds
  736.   Time to read WRITE_1.DAT into buffer:          520  milliseconds
  737.   Time to create, write and close WRITE_2.DAT:   115  milliseconds
  738.  
  739. SPEEDTST.TTP Execution Results
  740. for PRG_2DP.TOS, loaded from drive: G
  741.  
  742.   Load time:         55 milliseconds
  743.   Execution time:  1025 milliseconds
  744.  
  745. PRG_2DP.TOS Execution Results: ST Standard Formatted.
  746.  
  747.   Time to create, write and close WRITE_1.DAT:    3185  milliseconds
  748.   Time to read WRITE_1.DAT into buffer 10 times:   540  milliseconds
  749.   Time to create, write and close WRITE_2.DAT:    2650  milliseconds
  750.  
  751. SPEEDTST.TTP Execution Results
  752. for PRG_2DP.TOS, loaded from drive: A
  753.  
  754.   Load time:        385 milliseconds
  755.   Execution time:  7840 milliseconds
  756.  
  757. PRG_2DP.TOS Execution Results: Twister Formatted, Version 2.0.
  758.  
  759.   Time to create, write and close WRITE_1.DAT:    3195  milliseconds
  760.   Time to read WRITE_1.DAT into buffer 10 times:   540  milliseconds
  761.   Time to create, write and close WRITE_2.DAT:    2260  milliseconds
  762.  
  763. SPEEDTST.TTP Execution Results
  764. for PRG_2DP.TOS, loaded from drive: A
  765.  
  766.   Load time:        365 milliseconds
  767.   Execution time:  7300 milliseconds
  768.  
  769. PRG_2DP.TOS Execution Results: DC Formatter, Version 2.2.
  770.  
  771.   Time to create, write and close WRITE_1.DAT:    3185  milliseconds
  772.   Time to read WRITE_1.DAT into buffer 10 times:   540  milliseconds
  773.   Time to create, write and close WRITE_2.DAT:    2255  milliseconds
  774.  
  775. SPEEDTST.TTP Execution Results
  776. for PRG_2DP.TOS, loaded from drive: A
  777.  
  778.   Load time:        365 milliseconds
  779.   Execution time:  7275 milliseconds
  780.  
  781.  
  782.      Figure 6.2 shows the execution results of program 25 
  783. with the contents of memory location $444 equal to zero, 
  784. thereby disabling the write-verify function.  Notice that 
  785. the state of the write-verify function does not seem to 
  786. effect disk access times when the program is executed from a 
  787. hard disk partition.  The hard disk times of figures 6.1 and 
  788. 6.2 are identical, considering the 5 msec resolution of the 
  789. system clock.
  790.  
  791. Figure 6.2. Program 25's execution results with write-
  792. verify disabled.
  793.  
  794. PRG_2DP.TOS Execution Results: Hard disk drive.
  795.  
  796.   Time to create, write and close WRITE_1.DAT:     160  milliseconds
  797.   Time to read WRITE_1.DAT into buffer 10 times:   520  milliseconds
  798.   Time to create, write and close WRITE_2.DAT:     110  milliseconds
  799.  
  800. SPEEDTST.TTP Execution Results
  801. for PRG_2DP.TOS, loaded from drive: G
  802.  
  803.   Load time:         60 milliseconds
  804.   Execution time:  1020 milliseconds
  805.   
  806. PRG_2DP.TOS Execution Results: ST Standard Formatted.  
  807.   
  808.   Time to create, write and close WRITE_1.DAT:    1590  milliseconds
  809.   Time to read WRITE_1.DAT into buffer 10 times:   540  milliseconds
  810.   Time to create, write and close WRITE_2.DAT:    1450  milliseconds
  811.  
  812. SPEEDTST.TTP Execution Results
  813. for PRG_2DP.TOS, loaded from drive: A
  814.  
  815.   Load time:        390 milliseconds
  816.   Execution time:  4640 milliseconds
  817.   
  818. PRG_2DP.TOS Execution Results: Twister Formatted, Version 2.0.
  819.  
  820.   Time to create, write and close WRITE_1.DAT:    1595  milliseconds
  821.   Time to read WRITE_1.DAT into buffer 10 times:   540  milliseconds
  822.   Time to create, write and close WRITE_2.DAT:    1060  milliseconds
  823.  
  824. SPEEDTST.TTP Execution Results
  825. for PRG_2DP.TOS, loaded from drive: B
  826.  
  827.   Load time:        365 milliseconds
  828.   Execution time:  4100 milliseconds
  829.  
  830. PRG_2DP.TOS Execution Results: DC Formatter, Version 2.2.
  831.  
  832.   Time to create, write and close WRITE_1.DAT:    1590  milliseconds
  833.   Time to read WRITE_1.DAT into buffer 10 times:   540  milliseconds
  834.   Time to create, write and close WRITE_2.DAT:    1050  milliseconds
  835.  
  836. SPEEDTST.TTP Execution Results
  837. for PRG_2DP.TOS, loaded from drive: A
  838.  
  839.   Load time:        370 milliseconds
  840.   Execution time:  4080 milliseconds
  841.  
  842.  
  843. Unfinished Business: Pushing Zero Onto the Stack
  844.  
  845.      In chapter 2, I discussed the execution speed and 
  846. memory requirements of three ways to push a zero onto the 
  847. stack.  At that time, I used table 2.1 to list data that was 
  848. observed and prepared manually.  I am now going to introduce 
  849. a program that prints comparative data for the three methods 
  850. previously discussed.  Because the time required to execute 
  851. individual instructions is too short for accurate 
  852. measurement, we rely on multiple executions of each 
  853. instruction to lengthen the period of time that we are 
  854. measuring.  In addition, we are not as interested in 
  855. absolute execution speed as we are in relative speed.  We 
  856. just want to know which instructions are faster, not how 
  857. long it takes to execute one of them.
  858.      Program 28 will generate values that we can compare 
  859. objectively.  In addition, the program will calculate the 
  860. memory requirements for each of the three instructions in 
  861. which we are interested.  As a finale, the advantage of 
  862. using the third method, when it is appropriate to do so, is 
  863. illustrated by removal of the statement which needs be 
  864. executed only once from the execution loop.  That is the 
  865. statement which prestores a zero in register D0.
  866.  
  867. Program 28. Three ways to push zero onto the stack.
  868.  
  869.  ; Program Name:  PUSHZERO.S
  870.  ;      Version:  1.003
  871.  
  872.  ; Assembly Instructions:
  873.  
  874.  ;    Assemble in AssemPro PC-relative mode and save with a TOS extension.
  875.  
  876.  ; Execution Instructions:
  877.  
  878.  ;    Execute from the desktop; or execute SPAWN.TTP, type PUSHZERO.TOS on
  879.  ; its command line and view this program's output in PUSHZERO.DAT.
  880.  
  881.  ; Program Function:
  882.  
  883.  ; For each of the three methods of pushing $0 onto the stack discussed in
  884.  ; chapter two, calculates the memory occupied by each instruction, and
  885.  ; calculates the execution time in milliseconds required for 50,000
  886.  ; executions of each.
  887.  
  888.  ; Then, in order to emphasize the comparison for a more practical type of
  889.  ; application, the one statement in the third algorithm that needs be 
  890.  ; executed only once, before the execution of the loop, is placed outside
  891.  ; of the loop, and the time for 50,000 executions of the third method is
  892.  ; repeated.
  893.  
  894. calculate_program_size:
  895.  lea        -$102(pc), a1       ; Fetch basepage start address.
  896.  lea        program_end, a0     ; Fetch program end address.
  897.  adda.l     #100512, a0         ; Attach large stack space to end of program.
  898.  
  899.  ; NOTE: The above method of declaring stack space at the end of this program is
  900.  ;       preferable to that which I have been using because, if a label were
  901.  ;       used to declare the stack in the bss section, then the label used
  902.  ;       to mark the end of the program would be too far to permit the program
  903.  ;       to be assembled in AssemPro's PC-relative mode.
  904.  
  905.  movea.l    a0, a7              ; Point A7 to this program's stack.
  906.  trap       #6                  ; Return unused memory to op system.
  907.  
  908. print_heading:
  909.  lea        heading, a0
  910.  bsr        print_string
  911.  
  912. push_method_1:
  913.  lea        header_1, a0
  914.  bsr        print_string
  915.  move.l     #49999, d7          ; D7 is counter for the push loop.
  916.  trap       #3                  ; Fetch start time.
  917.  move.l     d0, d3              ; Save start_time in D3.          
  918. push_1_loop:                    ; Marks start of instruction in the loop.
  919.  clr.w      -(sp)               ; Instruction in the loop.
  920. memory_1:                       ; Marks end of instruction in the loop.
  921.  dbra       d7, push_1_loop     ; Loop 50000 times.
  922.  trap       #3                  ; Fetch end time.
  923.  sub.l      d3, d0              ; Subtract start time from end time.
  924.  trap       #10                 ; Convert to decimal milliseconds and print.
  925.  adda.l     #100000, sp         ; Reset stack pointer to top of stack.
  926.  
  927. print_method_1_requisite_memory:
  928.  lea        header_5, a0        ; Print requisite memory header.
  929.  bsr        print_string
  930.  lea        memory_1, a0        ; Calculate number of bytes occupied by the
  931.  lea        push_1_loop, a1     ; instruction in the loop.
  932.  suba.l     a1, a0
  933.  bsr        print_requisite_memory
  934.                        
  935. push_method_2:
  936.  lea        header_2, a0
  937.  bsr        print_string
  938.  move.l     #49999, d7          ; D7 is counter for the push loop.
  939.  trap       #3                  ; Fetch start time.
  940.  move.l     d0, d3              ; Save start_time in D3.
  941. push_2_loop:                    ; Marks start of instruction in the loop.   
  942.  move.w     #0, -(sp)           ; Instruction in the loop.
  943. memory_2:                       ; Marks end of instruction in the loop.
  944.  dbra       d7, push_2_loop     ; Loop 50000 times.
  945.  trap       #3                  ; Fetch end time.
  946.  sub.l      d3, d0              ; Subtract start time from end time.
  947.  trap       #10                 ; Convert to decimal milliseconds and print.
  948.  adda.l     #100000, sp         ; Reset stack pointer to top of stack.
  949.  
  950. print_method_2_requisite_memory:
  951.  lea        header_6, a0        ; Print requisite memory header.
  952.  bsr.s      print_string
  953.  lea        memory_2, a0        ; Calculate number of bytes occupied by the
  954.  lea        push_2_loop, a1     ; instruction in the loop.
  955.  suba.l     a1, a0
  956.  bsr.s      print_requisite_memory
  957.  
  958. push_method_3:
  959.  lea        header_3, a0
  960.  bsr.s      print_string
  961.  move.l     #49999, d7          ; D7 is counter for the push loop.
  962.  trap       #3                  ; Fetch start time.
  963.  move.l     d0, d3              ; Save start_time in D3.
  964. push_3_loop:                    ; Marks start of instructions in the loop.
  965.  moveq      #0, d0              ; There are two instructions in the loop.
  966.  move.w     d0, -(sp)           ; 
  967. memory_3:                       ; Marks end of instructions in the loop.
  968.  dbra       d7, push_3_loop     ; Loop 50000 times.
  969.  trap       #3                  ; Fetch end time.
  970.  sub.l      d3, d0              ; Subtract start time from end time.
  971.  trap       #10                 ; Convert to decimal milliseconds and print.
  972.  adda.l     #100000, sp         ; Reset stack pointer to top of stack.
  973.  
  974. print_method_3_requisite_memory:
  975.  lea        header_7, a0        ; Print requisite memory header.
  976.  bsr.s      print_string
  977.  lea        memory_3, a0        ; Calculate number of bytes occupied by the
  978.  lea        push_3_loop, a1     ; instructions in the loop.
  979.  suba.l     a1, a0
  980.  bsr.s      print_requisite_memory
  981.  
  982. modified_push_method_3:
  983.  lea        header_4, a0
  984.  bsr.s      print_string
  985.  move.l     #49999, d7          ; D7 is counter for the push loop.
  986.  trap       #3                  ; Fetch start time.
  987.  move.l     d0, d3              ; Save start_time in D3.
  988.  moveq      #0, d0              ; Prestore $0 in D0.
  989. push_4_loop:
  990.  move.w     d0, -(sp)           ; Contents of D0 onto the stack.
  991.  dbra       d7, push_4_loop     ; Loop 50000 times.
  992.  trap       #3                  ; Fetch end time.
  993.  sub.l      d3, d0              ; Subtract start time from end time.
  994.  trap       #10                 ; Convert to decimal milliseconds and print.
  995.  adda.l     #100000, sp         ; Reset stack pointer to top of stack.
  996.  
  997. terminate:
  998.  trap       #8
  999.  
  1000.  ;
  1001.  ; SUBROUTINES
  1002.  ;
  1003.  
  1004. print_requisite_memory:
  1005.  move.l     a0, d1
  1006.  trap       #4
  1007.  bsr.s      print_string
  1008.  lea        header_8, a0
  1009.  bsr.s      print_string
  1010.  rts
  1011.  
  1012. print_string:                   ; Expects address of string to be in A0.
  1013.  move.l     a0, -(sp)           ; Push address of string onto stack.
  1014.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  1015.  trap       #1                  ; GEMDOS call
  1016.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  1017.  rts
  1018.  
  1019.  data
  1020. heading:         dc.b $D,$A,"PUSHZERO Execution Results",$D,$A,$D,$A,0
  1021. header_1:        dc.b "   Elapsed time for clr.w  -(sp):     ",0
  1022. header_2:        dc.b "   Elapsed time for move.w #0, -(sp): ",0
  1023. header_3:        dc.b "   Elapsed time for moveq  #0, d0",$D,$A
  1024.                  dc.b "                    move.w d0, -(sp): ",0
  1025. header_4:        dc.b "   Time for only    move.w d0, -(sp): ",0
  1026. header_5:        dc.b "   Requisite memory:                     ",0
  1027. header_6:        dc.b "   Requisite memory:                     ",0
  1028. header_7:        dc.b "   Requisite memory:                     ",0
  1029. header_8:        dc.b "  bytes",$D,$A,$D,$A,0
  1030.  bss
  1031.  align                       
  1032. program_end:     ds.l      0 
  1033.  end
  1034.  
  1035. PUSHZERO Execution Results
  1036.  
  1037.    Elapsed time for clr.w  -(sp):       180  milliseconds
  1038.    Requisite memory:                      2  bytes
  1039.  
  1040.    Elapsed time for move.w #0, -(sp):   155  milliseconds
  1041.    Requisite memory:                      4  bytes
  1042.  
  1043.    Elapsed time for moveq  #0, d0
  1044.                     move.w d0, -(sp):   155  milliseconds
  1045.    Requisite memory:                      4  bytes
  1046.  
  1047.    Time for only    move.w d0, -(sp):   125  milliseconds
  1048.  
  1049.  
  1050. Accuracy of the Results
  1051.  
  1052.      It is true that we are interested in relative results, 
  1053. as far as instruction execution time is concerned, but it is 
  1054. worth remembering that when we view the results of a 
  1055. PUSHZERO execution, the elapsed times are calculated to a 
  1056. resolution of approximately 10 msec (milliseconds); 5 msec 
  1057. for the first call to get_time and 5 for the second call.
  1058.      How does this affect the accuracy of the program's 
  1059. report?  Well, there are four cases to consider, each 
  1060. composed of two incidents, with each incident depending on 
  1061. whether we receive the time just before or just after the 
  1062. variable _hz_200 has been incremented.
  1063.  
  1064.      1. Both the first and second times are received just 
  1065.         after increment.  In this case true time equals 
  1066.         time, and loss of accuracy is due to overhead in 
  1067.         making the calls and the inherent accuracy of the 
  1068.         system clock.
  1069.  
  1070.      2. The first time is received immediately after 
  1071.         increment, the second is received immediately 
  1072.         before.  Then, true time equals time plus 5 msec 
  1073.         plus overhead.
  1074.  
  1075.      3. The first time is received immediately before 
  1076.         increment, the second is received immediately after.  
  1077.         Then, true time equals time minus 5 msec plus 
  1078.         overhead.
  1079.  
  1080.      4. Both the first time and second times are received 
  1081.         immediately before increment.  In this case true 
  1082.         time equals time plus overhead.
  1083.  
  1084.      If you execute PUSHZERO repeatedly, you should notice 
  1085. some differences in reported times.  For each of the timed 
  1086. events in the program, I have never seen a variance greater 
  1087. than 5 msec, however, the variance can be less than or 
  1088. greater than the most often reported value.  We should 
  1089. expect this to happen because the resolution of the elapsed 
  1090. time calculations is 10 msec.
  1091.      To compare the reported times with the instruction 
  1092. execution times given in the Motorola's reference book, we 
  1093. need only consider the instructions that form the loop.  For 
  1094. push_method_1, the instructions are:
  1095.  
  1096.      clr.w  -(sp)
  1097.   
  1098.      dbra   d7, push_1_loop
  1099.   
  1100. The clr.w instruction requires 14 clock periods, the dbra 
  1101. instruction requires 10.  Total clock periods for the loop 
  1102. is 24 X 50,000 = 1.2X106.  Total time required to execute 
  1103. the loop is 1.2X106 X 1.25X10-7(time for one clock period) = 
  1104. 150 msec.  The program reports the time most often as 180 
  1105. msec.  The difference is due to inaccuracies in the system 
  1106. clock, the imprecise resolution and the time needed to 
  1107. execute non-related instructions.
  1108.  
  1109. Increasing Loop Execution Speed
  1110.   
  1111.      My primary use of program 28 was to introduce a method 
  1112. of determining the relative execution speed and memory 
  1113. requirements of two or more algorithms so that these 
  1114. attributes could be compared.  As an adjunct to the three 
  1115. primary algorithms, I took the opportunity to introduce the 
  1116. modified_push_method_3 algorithm.  With this algorithm, I 
  1117. intended to show you an example for which the third method 
  1118. of pushing a zero onto the stack was definitely superior 
  1119. because it clearly decreased loop execution time by 
  1120. decreasing the number of instructions within the loop.
  1121.      The next program, CLR_MEM, extends the idea of 
  1122. increasing loop execution speed, not by decreasing the 
  1123. instructions within the loop, but by increasing them.  We 
  1124. begin the example by considering the task of clearing 32000 
  1125. bytes of memory, the size of a video screen, to zero.  
  1126. Having been conditioned to consider quantities of memory in 
  1127. terms of bytes, the thought of clearing it a byte at a time 
  1128. should occur naturally.    Program 29 also illustrates the 
  1129. advantages to be gained by thinking of memory in terms of 
  1130. words and longwords.
  1131.   
  1132. Program 29. A program that clears 32,000 bytes of ram.
  1133.   
  1134.  ; Program Name: CLR_MEM.S
  1135.  ;      Version: 1.004
  1136.  
  1137.  ; Assembly Instructions:
  1138.  
  1139.  ;     Assemble in PC-relative mode save with a TOS extension.
  1140.  
  1141.  ; Execution Instructions:
  1142.  
  1143.  ;     Execute from the desktop; or execute SPAWN.TTP, type CLR_MEM.TOS on
  1144.  ; its command line and view this program's output in CLR_MEM.DAT.
  1145.  
  1146.  ; Program Function:
  1147.  
  1148.  ;     Expands the concepts established with program PUSHZERO.  Here,
  1149.  ; simulating the type of algorithm that would be used to clear video screen
  1150.  ; memory, the execution time when clearing memory a longword each time,
  1151.  ; within a loop, is compared to that of the more obvious method of clearing
  1152.  ; a byte, or perhaps a word, each time through the loop.
  1153.  
  1154.  ;     Then, the speed advantage of clearing a longword by storing the
  1155.  ; content of a cleared register is illustrated.
  1156.  
  1157.  ;     Finally, the advantage of clearing more than one longword within the
  1158.  ; body of the loop is explored, with an eye out for the maximum amount of
  1159.  ; beneficial loop expansion.
  1160.  
  1161. calculate_program_size:
  1162.  lea        -$102(pc), a1       ; Fetch basepage start address.
  1163.  lea        array, a0           ; Fetch program end = array address.
  1164.  adda.l     #32000, a0          ; Add in array space.
  1165.  movea.l    a0, a7              ; Point A7 to this program's stack.
  1166.  trap       #6                  ; Return unused memory to op system.
  1167.  
  1168. print_heading:
  1169.  lea        heading, a0
  1170.  bsr        print_string
  1171.  
  1172. clear_one_byte_algorithm:
  1173.  lea        header_1, a0
  1174.  bsr        print_string
  1175.  lea        array, a0           ; A0 is pointer to 32000 byte array.
  1176.  move.l     #31999, d7          ; D7 is counter for the clear loop.
  1177.  trap       #3                  ; Fetch start time.
  1178.  move.l     d0, d3              ; Save start_time in D3.
  1179. clear_a_byte:
  1180.  move.b     #0, (a0)+          
  1181.  dbra       d7, clear_a_byte    ; Loop 32000 times.
  1182.  trap       #3                  ; Fetch end time.
  1183.  sub.l      d3, d0              ; Subtract start time from end time.
  1184.  trap       #10                 ; Convert to decimal milliseconds and print.
  1185.  
  1186. clear_one_word_algorithm:
  1187.  lea        header_2, a0
  1188.  bsr        print_string
  1189.  lea        array, a0           ; A0 is pointer to 32000 byte array.
  1190.  move.l     #15999, d7          ; D7 is counter for the clear loop.
  1191.  trap       #3                  ; Fetch start time.
  1192.  move.l     d0, d3              ; Save start_time in D3.
  1193. clear_a_word:
  1194.  move.w     #0, (a0)+          
  1195.  dbra       d7, clear_a_word    ; Loop 16000 times.
  1196.  trap       #3                  ; Fetch end time.
  1197.  sub.l      d3, d0              ; Subtract start time from end time.
  1198.  trap       #10                 ; Convert to decimal milliseconds and print.
  1199.  
  1200. clear_one_longword_algorithm:
  1201.  lea        header_3, a0
  1202.  bsr        print_string
  1203.  lea        array, a0
  1204.  move.l     #7999, d7
  1205.  trap       #3                  ; Fetch start time.
  1206.  move.l     d0, d3              ; Save start_time in D3.          
  1207. clear_a_longword:
  1208.  move.l     #0, (a0)+          
  1209.  dbra       d7, clear_a_longword; Loop 8000 times.
  1210.  trap       #3                  ; Fetch end time.
  1211.  sub.l      d3, d0              ; Subtract start time from end time.
  1212.  trap       #10                 ; Convert to decimal milliseconds and print.
  1213.  
  1214. clear_one_longword_with_precleared_register:
  1215.  lea        header_4, a0
  1216.  bsr        print_string
  1217.  lea        array, a0
  1218.  moveq      #0, d1              ; Preclear D1 for use in the loop.
  1219.  move.l     #7999, d7
  1220.  trap       #3                  ; Fetch start time.
  1221.  move.l     d0, d3              ; Save start_time in D3.          
  1222. clear_with_register:
  1223.  move.l     d1, (a0)+           ; Clear a longword.
  1224.  dbra       d7, clear_with_register
  1225.  trap       #3                  ; Fetch end time.
  1226.  sub.l      d3, d0              ; Subtract start time from end time.
  1227.  trap       #10                 ; Convert to decimal milliseconds and print.
  1228.  
  1229. clear_4_longwords_algorithm:
  1230.  lea        header_5, a0
  1231.  bsr.s      print_string
  1232.  lea        array, a0
  1233.  move.l     #0, d1              ; Preclear D1 for use in the loop.
  1234.  move.l     #1999, d7
  1235.  trap       #3                  ; Fetch start time.
  1236.  move.l     d0, d3              ; Save start_time in D3.         
  1237. clr_4_longwords:          
  1238.  move.l     d1, (a0)+           ; A single move statement clears 4 bytes.
  1239.  move.l     d1, (a0)+           ; Reduces number of loops to 4000.
  1240.  move.l     d1, (a0)+           ; Reduces number of loops to 2666+.
  1241.  move.l     d1, (a0)+           ; Reduces number of loops to 2000.
  1242.  dbra       d7, clr_4_longwords ; Loop 2000 times, clear 16 bytes each time.
  1243.  trap       #3                  ; Fetch end time.
  1244.  sub.l      d3, d0              ; Subtract start time from end time.
  1245.  trap       #10                 ; Convert to decimal milliseconds and print.
  1246.  
  1247. clear_8_longwords_algorithm:    ; Will a further increase in move instructions
  1248.  lea        header_6, a0        ; within the loop decrease execution time?
  1249.  bsr.s      print_string
  1250.  lea        array, a0  
  1251.  move.l     #0, d1              ; Preclear D1 for use in the loop.
  1252.  move.l     #999, d7
  1253.  trap       #3                  ; Fetch start time.
  1254.  move.l     d0, d3              ; Save start_time in D3.
  1255. clr_8_longwords:
  1256.  move.l     d1, (a0)+           ; A single move statement clears 4 bytes.
  1257.  move.l     d1, (a0)+           ; Reduces number of loops to 4000.
  1258.  move.l     d1, (a0)+           ; Reduces number of loops to 2666+.
  1259.  move.l     d1, (a0)+           ; Reduces number of loops to 2000.
  1260.  move.l     d1, (a0)+           ; Reduces number of loops to 1600.
  1261.  move.l     d1, (a0)+           ; Reduces number of loops to 1333+.
  1262.  move.l     d1, (a0)+           ; Reduces number of loops to 1142+.
  1263.  move.l     d1, (a0)+           ; Reduces number of loops to 1000.
  1264.  dbra       d7, clr_8_longwords ; Loop 1000 times, clear 32 bytes each time.
  1265.  trap       #3                  ; Fetch end time.
  1266.  sub.l      d3, d0              ; Subtract start time from end time.
  1267.  trap       #10                 ; Convert to decimal milliseconds and print.
  1268.  
  1269. terminate:
  1270.  trap       #8
  1271.  
  1272.  ;
  1273.  ; SUBROUTINES
  1274.  ;
  1275.  
  1276. print_string:                   ; Expects address of string to be in A0.
  1277.  move.l     a0, -(sp)           ; Push address of string onto stack.
  1278.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  1279.  trap       #1                  ; GEMDOS call
  1280.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  1281.  rts
  1282.  
  1283.  data
  1284. heading:        dc.b 'CLR_MEM Execution Results',$D,$A,0
  1285. header_1:       dc.b '   Clear one byte time:     ',0
  1286. header_2:       dc.b '   Clear one word time:     ',0
  1287. header_3:       dc.b '   Clear one longword time: ',0
  1288. header_4:       dc.b '   With register time:      ',0
  1289. header_5:       dc.b '   Clear 4 longwords time:  ',0
  1290. header_6:       dc.b '   Clear 8 longwords time:  ',0
  1291.  bss
  1292.  align            
  1293. start_time:      ds.l     1
  1294.                  ds.l    96    ; Stack.
  1295. stack:           ds.l     1    ; Address of stack.
  1296. array:           ds.l     0
  1297.  end
  1298.  
  1299. CLR_MEM Execution Results
  1300.    Clear one byte time:       100  milliseconds
  1301.    Clear one word time:        45  milliseconds
  1302.    Clear one longword time:    35  milliseconds
  1303.    With register time:         25  milliseconds
  1304.    Clear 4 longwords time:     15  milliseconds
  1305.    Clear 8 longwords time:     10  milliseconds
  1306.  
  1307.  
  1308.      If you assemble the program and repeatedly execute it, 
  1309. you will occasionally obtain 95 msec for the one byte time, 
  1310. 50 msec for the one word time, 15 msec for the 8 longwords 
  1311. time and etc.  You know that variances are possible because 
  1312. of your experience with program 28, so don't let the 
  1313. variances bother you.  The important things to realize are 
  1314. these:
  1315.  
  1316.      1. Clearing the 32000 bytes a word at a time is just 
  1317.         twice as fast as clearing it a byte at a time.  This 
  1318.         is so only because we were able to reduce the number 
  1319.         of times through the loop by half.  The amount of 
  1320.         time to clear one word of memory is exactly equal to 
  1321.         the time it takes to clear one byte.
  1322.  
  1323.      2. The time it took to clear the 32000 bytes a longword 
  1324.         at a time is not one/fourth the time it took to 
  1325.         clear a byte, even though the number of loops was 
  1326.         reduced by four, because it takes more time to clear 
  1327.         a longword of memory than it does to clear a byte or 
  1328.         a word.
  1329.  
  1330.      3. Clearing a register to zero before we enter the 
  1331.         loop, then storing the content of the register is 
  1332.         faster than using a move.l #0 instruction within the 
  1333.         loop.
  1334.  
  1335.      4. Storing more than one longword within the loop 
  1336.         reduced the loop execution time significantly.  
  1337.         However, the times reported for the 
  1338.         clear_four_longwords and clear_eight_longwords 
  1339.         algorithms are too close to the resolution of the 
  1340.         time recording functions; therefore, the 
  1341.         significance of the reported values is questionable.
  1342.  
  1343.      Even though the results obtained for the longword 
  1344. algorithms mentioned in item four are not entirely reliable, 
  1345. they seem to suggest that, as the number of instructions 
  1346. within the loop is increased, we will begin to notice a 
  1347. saturation effect; that is, a reduction in the number of 
  1348. loops will affect the overall loop execution time to a 
  1349. lesser degree because of the amount of time spent within the 
  1350. loop.
  1351.      After I had seen the results of CLR_MEM, I could have 
  1352. rewritten the program so that it would produce more 
  1353. palatable results, however, if I had done that, I would have 
  1354. denied you the experience of viewing a program which 
  1355. produces valid results for only a portion of its execution.  
  1356. You would not know that I had not been completely successful 
  1357. the first time.
  1358.      In doing something to validate the results for the last 
  1359. two algorithms of program 29, one would be easily tempted to 
  1360. simply increase the number of times through each loop in the 
  1361. program by an identical factor, say 10, perhaps.  In fact, 
  1362. it is the first method of improvement that occurred to me.  
  1363. When it didn't work, I was forced to examine the details of 
  1364. the DBRA instruction, something I had not done in a while.
  1365.      If you refer back to program 29, you can see that, if 
  1366. we increase the size of memory to be cleared by a factor of 
  1367. 10, we must clear 320,000 bytes in the clear_one_byte 
  1368. algorithm.  To do that, we must loop 320,000 times, 
  1369. therefore, the value 319,999 must be prestored in the loop 
  1370. counter.  What is wrong with this picture?  The DBRA 
  1371. instruction (as does all of the DBcc instructions) permits 
  1372. its counter parameter to contain only a 16-bit value.
  1373.      The maximum count we can fit in 16 bits is 65535.  So 
  1374. we see why we can't set the counter to 320,000 and expect 
  1375. valid results.  Similarly, we know that a value of 159,999 
  1376. for the clear_one_word algorithm will not work, nor will a 
  1377. value of 79,999 work for the clear_one_longword algorithm.  
  1378. In order to rearrange CLR_MEM so that the results for each 
  1379. algorithm would be comparable, we would have to resort to a 
  1380. minor loop inside of a major loop.  Preparing CLR_MEM2, by 
  1381. extracting the necessary portions from PRGM_1F and 
  1382. increasing the appropriate values, is a more elegant 
  1383. solution.  And, while we are doing that, we may as well 
  1384. include an algorithm that will let us obtain another 
  1385. saturation effect observation point.
  1386.  
  1387. Program 30. Measures the time to clear memory with greater 
  1388. accuracy.
  1389.  
  1390.  ; Program Name: CLR_MEM2.S
  1391.  ;      Version: 1.002
  1392.  
  1393.  ; Assembly Instructions:
  1394.  
  1395.  ;    Assemble in PC-relative mode and save with a TOS extension.
  1396.  
  1397.  ; Execution Instructions:
  1398.  
  1399.  ;    Execute SPAWN.TTP and type CLR_MEM2.TOS on its command line.  View the
  1400.  ; output of this program in CLR_MEM2.DAT.
  1401.  
  1402.  ; Program Function:
  1403.  
  1404.  ; The four_longwords_time and the eight_longwords_time reported by program
  1405.  ; CLR_MEM are too short to be reliable.  Both values are too close to
  1406.  ; the resolution of the time recording functions.
  1407.  
  1408.  ; In this program, the significance of the reported execution times is 
  1409.  ; increased by increasing the number of loops for those two algorithms
  1410.  ; by a factor of 10.  This will permit a more valid comparison between the
  1411.  ; two algorithms.
  1412.  
  1413.  ; In addition, since I am going through the trouble of preparing this
  1414.  ; special program, I include one more algorithm.  This one to investigate
  1415.  ; the intermediate five_longwords_time.
  1416.  
  1417.  ; Why go through this trouble?  Well, as the number of instructions within
  1418.  ; the loop increases, we should observe a saturation effect in execution
  1419.  ; speed reduction, even though the number of loops are decreased, simply
  1420.  ; because more time is spent inside the loop.  This additional point of
  1421.  ; observation will help to confirm or dismiss that theory.
  1422.  
  1423.  ; Now, because we are increasing the number of loops by 10 in this program,
  1424.  ; if we want to compare the results we obtain with those obtained from
  1425.  ; program CLR_MEM, we will have to divide CLR_MEM2's results by 10.
  1426.  
  1427. calculate_program_size:
  1428.  lea        -$102(pc), a1       ; Fetch basepage start address.
  1429.  lea        program_end, a0     ; Fetch program end address.
  1430.  adda.l     #320000, a0         ; Add in array space.
  1431.  movea.l    a0, a7              ; Point A7 to this program's stack.
  1432.  trap       #6                  ; Return unused memory to op system.
  1433.  
  1434. print_heading:
  1435.  lea        heading, a0
  1436.  bsr        print_string
  1437.  
  1438. clear_4_longwords_algorithm:
  1439.  lea        header_1, a0
  1440.  bsr        print_string
  1441.  lea        array, a0
  1442.  move.l     #0, d1              ; Preclear D1 for use in the loop.
  1443.  move.l     #19999, d7
  1444.  trap       #3                  ; Fetch start time.
  1445.  move.l     d0, d3              ; Save start_time in D3.         
  1446. clr_4_longwords:
  1447.  move.l     d1, (a0)+           ; A single move statement clears 4 bytes.
  1448.  move.l     d1, (a0)+           ; Reduces number of loops to 40000.
  1449.  move.l     d1, (a0)+           ; Reduces number of loops to 26666+.
  1450.  move.l     d1, (a0)+           ; Reduces number of loops to 20000.
  1451.  dbra       d7, clr_4_longwords ; Loop 20000 times, clear 16 bytes each time.          
  1452.  trap       #3                  ; Fetch end time.
  1453.  sub.l      d3, d0              ; Subtract start time from end time.
  1454.  trap       #10                 ; Convert to decimal milliseconds and print.
  1455.  
  1456. clear_5_longwords_algorithm:
  1457.  lea        header_2, a0
  1458.  bsr.s      print_string
  1459.  lea        array, a0
  1460.  move.l     #0, d1              ; Preclear D1 for use in the loop.
  1461.  move.l     #15999, d7
  1462.  trap       #3                  ; Fetch start time.
  1463.  move.l     d0, d3              ; Save start_time in D3.
  1464. clr_5_longwords:          
  1465.  move.l     d1, (a0)+           ; A single move statement clears 4 bytes.
  1466.  move.l     d1, (a0)+           ; Reduces number of loops to 40000.
  1467.  move.l     d1, (a0)+           ; Reduces number of loops to 26666+.
  1468.  move.l     d1, (a0)+           ; Reduces number of loops to 20000.
  1469.  move.l     d1, (a0)+           ; Reduces number of loops to 16000.
  1470.  dbra       d7, clr_5_longwords ; Loop 16000 times, clear 16 bytes each time.
  1471.  trap       #3                  ; Fetch end time.
  1472.  sub.l      d3, d0              ; Subtract start time from end time.
  1473.  trap       #10                 ; Convert to decimal milliseconds and print.
  1474.  
  1475. clear_8_longwords_algorithm:
  1476.  lea        header_3, a0
  1477.  bsr.s      print_string
  1478.  lea        array, a0          
  1479.  move.l     #0, d1              ; Preclear D1 for use in the loop.
  1480.  move.l     #9999, d7
  1481.  trap       #3                  ; Fetch start time.
  1482.  move.l     d0, d3              ; Save start_time in D3.
  1483. clr_8_longwords:
  1484.  move.l     d1, (a0)+           ; A single move statement clears 4 bytes.
  1485.  move.l     d1, (a0)+           ; Reduces number of loops to 40000.
  1486.  move.l     d1, (a0)+           ; Reduces number of loops to 26666+.
  1487.  move.l     d1, (a0)+           ; Reduces number of loops to 20000.
  1488.  move.l     d1, (a0)+           ; Reduces number of loops to 16000.
  1489.  move.l     d1, (a0)+           ; Reduces number of loops to 13333+.
  1490.  move.l     d1, (a0)+           ; Reduces number of loops to 11428+.
  1491.  move.l     d1, (a0)+           ; Reduces number of loops to 10000.
  1492.  dbra       d7, clr_8_longwords ; Loop 10000 times, clear 32 bytes each time.
  1493.  trap       #3                  ; Fetch end time.
  1494.  sub.l      d3, d0              ; Subtract start time from end time.
  1495.  trap       #10                 ; Convert to decimal milliseconds and print.
  1496.  
  1497. terminate:
  1498.  trap       #8
  1499.  
  1500.  ;
  1501.  ; SUBROUTINES
  1502.  ;
  1503.  
  1504. print_string:                   ; Expects address of string to be in A0.
  1505.  move.l     A0, -(sp)           ; Push address of string onto stack.
  1506.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  1507.  trap       #1                  ; GEMDOS call
  1508.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  1509.  rts
  1510.  
  1511.  data
  1512. heading:        dc.b 'CLR_MEM2 Execution Results',$D,$A,0
  1513. header_1:       dc.b '   Clear 4 longwords time: ',0
  1514. header_2:       dc.b '   Clear 5 longwords time: ',0
  1515. header_3:       dc.b '   Clear 8 longwords time: ',0
  1516.  bss
  1517.  align                                    ; Align storage on a word boundary.
  1518.                             ds.l    24    ; Stack.
  1519. stack:                      ds.l     0    ; Address of stack.
  1520. program_end:                ds.l     0    ; Marks the end of program memory.
  1521. array:                      ds.l     0
  1522.  end
  1523.  
  1524. CLR_MEM2 Execution Results
  1525.    Clear 4 longwords time:   155  milliseconds
  1526.    Clear 5 longwords time:   145  milliseconds
  1527.    Clear 8 longwords time:   140  milliseconds
  1528.  
  1529.  
  1530.      Program 30's results indicate that those of program 29 
  1531. were actually valid enough.  For if we divide the values 
  1532. reported by CLR_MEM2 by 10, we obtain 15.5, 15.0 and 14.0.  
  1533. In addition, this is the kind of data grouping we observe at 
  1534. a saturation level.  Program 30's report gives us the 
  1535. confidence we need to utilize the clear_4_longwords 
  1536. algorithm to clear the video screen, knowing that we are 
  1537. prudently compromising between execution speed and memory 
  1538. requirement.
  1539.  
  1540. Unfinished Business: The Morton Conclusion
  1541.   
  1542.      As promised at the end of chapter 3, I will resume my 
  1543. discussion of Mr. Morton's conclusion concerning 
  1544. multiplication.  Program 31 records the relative speeds of 
  1545. the two methods of multiplying a word operand by four; 
  1546. however, I have added a little spice because, not only are 
  1547. the execution speeds for the two methods identical, but the 
  1548. shift method is the faster when multiplying a longword 
  1549. operand by four.  Furthermore, the shift method requires 
  1550. less memory.  I should also state that the adding dn to 
  1551. itself twice is faster than shifting left twice theory is 
  1552. supported in the Kelly-Bootle book on page 202.
  1553.  
  1554. Program 31.  The refutation.
  1555.  
  1556.  ; Program Name: TIMES_4.S
  1557.  ;      Version: 1.005
  1558.  
  1559.  ; Assembly Instructions:
  1560.  
  1561.  ;     Assemble in PC-relative mode and save with a TOS extension.
  1562.  
  1563.  ; Execution Instructions:
  1564.  
  1565.  ;     Execute from the desktop; or execute SPAWN.TTP, type TIMES_4.TOS on
  1566.  ; its command line and view this program's output in TIMES_4.DAT.
  1567.  
  1568.  ; Program Function:
  1569.  
  1570.  ;     Compares the speed of multiplying by 4 with add dn,dn to asl #2,dn
  1571.  ; to determine which is faster.  This experiment attempts to prove or
  1572.  ; refute the conclusion advanced by Mike Morton in his magazine article,
  1573.  ; "68000 Tricks and Traps", Byte, September, 1986, p163.  As it turns out,
  1574.  ; when multiplying a word operand by 4, as Mr. Morton specifies in his
  1575.  ; article, the execution times are identical.  But when multiplying a
  1576.  ; longword by 4, the shift algorithm is faster.  Furthermore, the shift
  1577.  ; algorithm requires less memory.
  1578.  
  1579.  ;     Author Stan Kelly-Bootle also asserts that the add.w dn,dn algorithm
  1580.  ; is a faster method of multiplying by 4 than is asl #2,dn at the top of
  1581.  ; page 202 of his book "680x0 Programming by Example".
  1582.  
  1583. calculate_program_size:
  1584.  lea        -$102(pc), a1       ; Fetch basepage start address.  
  1585.  lea        program_end, a0     ; Fetch program end address.
  1586.  trap       #6                  ; Return unused memory to op system.
  1587.  
  1588. print_heading:
  1589.  lea        heading, a0
  1590.  bsr        print_string
  1591.  
  1592. word_addition:
  1593.  lea        header_1, a0     
  1594.  bsr        print_string
  1595.  move.l     #49999, d7          ; D7 is counter for the loop.
  1596.  trap       #3                  ; Fetch start time.
  1597.  move.l     d0, d3              ; Save start_time in D3.
  1598. word_addition_loop:             ; Marks start of instruction in the loop.
  1599.  add.w      d0, d0              ; To multiply by two.
  1600.  add.w      d0, d0              ; To multiply by four.
  1601. memory_1:                       ; Marks end of instruction in the loop.
  1602.  dbra       d7, word_addition_loop
  1603.  trap       #3                  ; Fetch end time.
  1604.  sub.l      d3, d0              ; Subtract start time from end time.
  1605.  trap       #10                 ; Convert to decimal milliseconds and print.
  1606.  
  1607. print_word_addition_requisite_memory:
  1608.  lea        header_5, a0
  1609.  bsr        print_string
  1610.  lea        memory_1, a0           ; Calculate number of bytes occupied by
  1611.  lea        word_addition_loop, a1 ; the instructions in the loop.
  1612.  suba.l     a1, a0
  1613.  bsr        print_requisite_memory
  1614.  
  1615. word_shift:
  1616.  lea        header_2, a0     
  1617.  bsr        print_string
  1618.  move.l     #49999, d7          ; D7 is counter for the loop.
  1619.  trap       #3                  ; Fetch start time.
  1620.  move.l     d0, d3              ; Save start_time in D3.
  1621. word_shift_loop:                ; Marks start of instruction in the loop.
  1622.  asl.w      #2, d0              ; Shift to multiply by 4.
  1623. memory_2:                       ; Marks end of instruction in the loop.
  1624.  dbra       d7, word_shift_loop ; Loop 50000 times.
  1625.  trap       #3                  ; Fetch end time.
  1626.  sub.l      d3, d0              ; Subtract start time from end time.
  1627.  trap       #10                 ; Convert to decimal milliseconds and print.
  1628.  
  1629. print_word_shift_requisite_memory:
  1630.  lea        header_6, a0
  1631.  bsr.s      print_string
  1632.  lea        memory_2, a0        ; Calculate number of bytes occupied by the
  1633.  lea        word_shift_loop, a1 ; instruction in the loop, then store.
  1634.  suba.l     a1, a0
  1635.  bsr.s      print_requisite_memory
  1636.  
  1637. longword_addition:
  1638.  lea        header_3, a0     
  1639.  bsr.s      print_string
  1640.  move.l     #49999, d7          ; D7 is counter for the loop.
  1641.  trap       #3                  ; Fetch start time.
  1642.  move.l     d0, d3              ; Save start_time in D3.
  1643. addition_loop:                  ; Marks start of instructions in the loop.
  1644.  add.l      d0, d0              ; To multiply by two.
  1645.  add.l      d0, d0              ; To multiply by four.
  1646. memory_3:                       ; Marks end of instruction in the loop.
  1647.  dbra       d7, addition_loop   ; Loop 50000 times.
  1648.  trap       #3                  ; Fetch end time.
  1649.  sub.l      d3, d0              ; Subtract start time from end time.
  1650.  trap       #10                 ; Convert to decimal milliseconds and print.
  1651.  
  1652. print_longword_addition_requisite_memory:
  1653.  lea        header_7, a0
  1654.  bsr.s      print_string
  1655.  lea        memory_3, a0        ; Calculate number of bytes occupied by the
  1656.  lea        addition_loop, a1   ; instruction in the loop, then store.
  1657.  suba.l     a1, a0
  1658.  bsr.s      print_requisite_memory
  1659.  
  1660. longword_shift:
  1661.  lea        header_4, a0     
  1662.  bsr.s      print_string
  1663.  move.l     #49999, d7          ; D7 is counter for the loop.
  1664.  trap       #3                  ; Fetch start time.
  1665.  move.l     d0, d3              ; Save start_time in D3.
  1666.  trap       #3                  ; Value of system clock returned in D0.
  1667.  move.l     d0, d1              ; Save in trap call register.
  1668. shift_loop:                     ; Marks start of instruction in the loop.
  1669.  asl.l      #2, d0              ; Shift to multiply by 4.
  1670. memory_4:                       ; Marks end of instruction in the loop.
  1671.  dbra       d7, shift_loop      ; Loop 50000 times.
  1672.  trap       #3                  ; Fetch end time.
  1673.  sub.l      d3, d0              ; Subtract start time from end time.
  1674.  trap       #10                 ; Convert to decimal milliseconds and print.
  1675.  
  1676. print_longword_shift_requisite_memory:
  1677.  lea        header_8, a0
  1678.  bsr.s      print_string
  1679.  lea        memory_4, a0        ; Calculate number of bytes occupied by the
  1680.  lea        shift_loop, a1      ; instruction in the loop, then store.
  1681.  suba.l     a1, a0
  1682.  bsr.s      print_requisite_memory
  1683.  
  1684. terminate:
  1685.  trap       #8
  1686.  
  1687.  ;
  1688.  ; SUBROUTINES
  1689.  ;
  1690.  
  1691. print_requisite_memory:
  1692.  move.l     a0, d1
  1693.  trap       #4
  1694.  bsr.s      print_string
  1695.  lea        header_9, a0
  1696.  bsr.s      print_string
  1697.  rts
  1698.  
  1699. print_string:                   ; Expects address of string to be in A0.
  1700.  move.l     a0, -(sp)           ; Push address of string onto stack.
  1701.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  1702.  trap       #1                  ; GEMDOS call
  1703.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  1704.  rts
  1705.  
  1706.  data
  1707. heading:      dc.b  "TIMES_4 Execution Results",$D,$A,$D,$A,0
  1708. header_1:     dc.b       "   Word addition time:           ",0
  1709. header_2:     dc.b $D,$A,"   Word shift time:              ",0
  1710. header_3:     dc.b $D,$A,"   Longword addition time:       ",0
  1711. header_4:     dc.b $D,$A,"   Longword shift time:          ",0
  1712. header_5:     dc.b       "   Word addition requisite memory:  ",0
  1713. header_6:     dc.b       "   Word shift requisite memory:     ",0
  1714. header_7:     dc.b       "   Longword add requisite memory:   ",0
  1715. header_8:     dc.b       "   Longword shift requisite memory: ",0
  1716. header_9:     dc.b       " bytes",$D,$A,0
  1717.  bss
  1718.  align
  1719. program_end:  ds.l   0   
  1720.  end
  1721.  
  1722.  
  1723. TIMES_4 Execution Results
  1724.  
  1725.    Word addition time:             130  milliseconds
  1726.    Word addition requisite memory:   4 bytes
  1727.  
  1728.    Word shift time:                125  milliseconds
  1729.    Word shift requisite memory:      2 bytes
  1730.  
  1731.    Longword addition time:         180  milliseconds
  1732.    Longword add requisite memory:    4 bytes
  1733.  
  1734.    Longword shift time:            155  milliseconds
  1735.    Longword shift requisite memory:  2 bytes
  1736.  
  1737. SPEEDTST.TTP Execution Results
  1738. for TIMES_4.TOS, loaded from drive: G
  1739.  
  1740.   Load time:         50 milliseconds
  1741.   Execution time:   925 milliseconds
  1742.  
  1743.  
  1744.      Now that the subject of multiplication algorithms has 
  1745. been introduced, it seems appropriate to present a program 
  1746. which compares the MC68000 multiply instruction to 
  1747. algorithms that accomplish the same task.  That this seems 
  1748. appropriate might seem enigmatic at first because 
  1749. multiplication instructions are part of the MC68000's 
  1750. repertoire.  Nevertheless, as is indicated in Mr. Morton's 
  1751. article, multiplication algorithms which ignore those 
  1752. instructions are faster for certain multipliers even if they 
  1753. are not powers of 2.
  1754.      Program 32 illustrates this fact for multiplication by 
  1755. 5, a multiplier that is used in many of my programs to 
  1756. convert the system clock count to milliseconds.  
  1757. MULTIPLY.TOS compares three methods of multiplying by 5.  
  1758. The program can be executed from the desktop or by typing 
  1759. its name on the command line of SPAWN.TTP or SPEEDTST.TTP.
  1760.   
  1761. Program 32. Compares the speed of three multiplication 
  1762. algorithms.
  1763.  
  1764.  ; Program Name: MULTIPLY.S
  1765.  ;      Version: 1.002
  1766.  
  1767.  ; Assembly Instructions:
  1768.  
  1769.  ;     Assemble in PC-relative mode and save with a TOS extension.
  1770.  
  1771.  ; Execution Instructions:
  1772.  
  1773.  ;     Execute from the desktop; or execute SPAWN.TTP, type MULTIPLY.TOS on
  1774.  ; its command line and view this program's output in MULTIPLY.DAT.
  1775.  
  1776.  ; Program Function:
  1777.  
  1778.  ;     Measures the speed of multiplication algorithms.
  1779.  
  1780. calculate_program_size:
  1781.  lea        -$102(pc), a1       ; Fetch basepage start address.  
  1782.  lea        program_end, a0     ; Fetch program end address.
  1783.  trap       #6                  ; Return unused memory to op system.
  1784.  
  1785. print_heading:
  1786.  lea        heading, a0
  1787.  bsr        print_string
  1788.  
  1789. the_mulu_instruction:
  1790.  lea        header_1, a0 
  1791.  bsr        print_string
  1792.  move.l     #49999, d7          ; D7 is counter for the loop.
  1793.  trap       #3                  ; Fetch start time.
  1794.  move.l     d0, d3              ; Save start_time in D3.
  1795. mulu_loop:                      ; Marks start of instruction in the loop.
  1796.  mulu       #5, d0              ; Instruction in the loop.
  1797. memory_1:                       ; Marks end of instruction in the loop.
  1798.  dbra       d7, mulu_loop       ; Loop 50000 times.
  1799.  trap       #3                  ; Fetch end time.
  1800.  sub.l      d3, d0              ; Subtract start time from end time.
  1801.  trap       #10                 ; Convert to decimal milliseconds and print.
  1802.  
  1803. addition:
  1804.  lea        header_2, a0     
  1805.  bsr.s      print_string
  1806.  move.l     #49999, d7          ; D7 is counter for the push loop.
  1807.  trap       #3                  ; Fetch start time.
  1808.  move.l     d0, d3              ; Save start_time in D3.
  1809. addition_loop:                  ; Marks start of instruction in the loop.
  1810.  move.l     d0, d2              ; To add one.   
  1811.  add.l      d0, d0              ; To double to two.
  1812.  add.l      d0, d0              ; To double to four.
  1813.  add.l      d2, d0              ; To complete multiplication by 5.
  1814. memory_2:                       ; Marks end of instruction in the loop.
  1815.  dbra       d7, addition_loop   ; Loop 50000 times.
  1816.  trap       #3                  ; Fetch end time.
  1817.  sub.l      d3, d0              ; Subtract start time from end time.
  1818.  trap       #10                 ; Convert to decimal milliseconds and print.
  1819.  
  1820. shift_and_add:
  1821.  lea        header_3, a0     
  1822.  bsr.s      print_string
  1823.  move.l     #49999, d7          ; D7 is counter for the push loop.
  1824.  trap       #3                  ; Fetch start time.
  1825.  move.l     d0, d3              ; Save start_time in D3.
  1826. shift_loop:                     ; Marks start of instruction in the loop.
  1827.  move.l     d0, d2              ; Save a copy to add.
  1828.  asl.l      #2, d0              ; Shift to multiply by 4. 
  1829.  add.l      d2, d0              ; To complete multiplication by 5.
  1830. memory_3:                       ; Marks end of instruction in the loop.
  1831.  dbra       d7, shift_loop      ; Loop 50000 times.
  1832.  trap       #3                  ; Fetch end time.
  1833.  sub.l      d3, d0              ; Subtract start time from end time.
  1834.  trap       #10                 ; Convert to decimal milliseconds and print.
  1835.  
  1836. print__mulu_requisite_memory:
  1837.  lea        header_4, a0
  1838.  bsr.s      print_string
  1839.  lea        memory_1, a0        ; Calculate number of bytes occupied by the
  1840.  lea        mulu_loop, a1       ; instruction in the loop.
  1841.  bsr.s      print_requisite_memory
  1842.  
  1843. print_addition_requisite_memory:
  1844.  lea        header_5, a0
  1845.  bsr.s      print_string
  1846.  lea        memory_2, a0        ; Calculate number of bytes occupied by the
  1847.  lea        addition_loop, a1   ; instruction in the loop.
  1848.  bsr.s      print_requisite_memory
  1849.  
  1850. printS_shift_and_add_requisite_memory:
  1851.  lea        header_6, a0
  1852.  bsr.s      print_string
  1853.  lea        memory_3, a0        ; Calculate number of bytes occupied by the
  1854.  lea        shift_loop, a1      ; instruction in the loop.
  1855.  bsr.s      print_requisite_memory
  1856.  
  1857. terminate:
  1858.  trap       #8
  1859.  
  1860.  ; SUBROUTINES
  1861.  
  1862. print_requisite_memory:
  1863.  suba.l     a1, a0
  1864.  move.l     a0, d1
  1865.  trap       #4                  ; Returns address of decimal string in A0.
  1866.  bsr.s      print_string
  1867.  lea        header_7, a0
  1868.  bsr.s      print_string
  1869.  rts
  1870.  
  1871. print_string:                   ; Expects address of string to be in A0.
  1872.  pea        (a0)                ; Push address of string onto stack.
  1873.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  1874.  trap       #1                  ; GEMDOS call
  1875.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  1876.  rts
  1877.  
  1878.  data
  1879. heading:      dc.b   "MULTIPLY Execution Results",$D,$A,$D,$A,0
  1880. header_1:     dc.b       "   MULU time:                    ",0
  1881. header_2:     dc.b       "   Addition time:                ",0
  1882. header_3:     dc.b       "   Shift and add time:           ",0
  1883. header_4:     dc.b $D,$A,"   MULU requisite memory:           ",0
  1884. header_5:     dc.b       "   Addition requisite memory:       ",0
  1885. header_6:     dc.b       "   Shift and add requisite memory:  ",0
  1886. header_7:     dc.b       "  bytes",$D,$A,0
  1887.  bss
  1888.  align                        
  1889. program_end:  ds.l      0  
  1890.  end
  1891.  
  1892. MULTIPLY Execution Results
  1893.  
  1894.    MULU time:                      360  milliseconds
  1895.    Addition time:                  260  milliseconds
  1896.    Shift and add time:             230  milliseconds
  1897.  
  1898.    MULU requisite memory:            4  bytes
  1899.    Addition requisite memory:        8  bytes
  1900.    Shift and add requisite memory:   6  bytes
  1901.   
  1902.   
  1903.      The results clearly show that the shift and add 
  1904. algorithm, in this case, is superior.  It is 1.5 times 
  1905. faster than the MULU instruction, while requiring only 2 
  1906. additional bytes of memory.  On page 168 of his article, Mr. 
  1907. Morton shows an example of multiplication by 17.
  1908.  
  1909. LEA Versus ADDA For Stack Adjustments
  1910.   
  1911.      On page 166 of the same Morton article is a nice tip 
  1912. concerning stack pointer movement.  Mr. Morton used so few 
  1913. words to describe this hint that it is easy to slide right 
  1914. by it while reading the article.  Program 33 explores this 
  1915. subject in a bit more detail, confirming that adjustments to 
  1916. the stack using the LEA instruction is twice as fast as 
  1917. adjustments using the ADDA instruction.  The reason that 
  1918. this comparison is of concern is that the ADDQ instruction 
  1919. can be used for stack adjustments only when the alteration 
  1920. is limited to a maximum of 8 bytes.  Beyond that, the ADDA 
  1921. instruction is mandatory.
  1922.      Within the documentation of program 33, I have included 
  1923. a timing note concerning the timing method I have been using 
  1924. and its relationship to the instruction execution times 
  1925. listed in the Motorola manual.  With suitable precautions, 
  1926. you may be able to used the information there to generate 
  1927. timing data that more closely conforms to the Motorola data 
  1928. if you should find it necessary or desirable.
  1929.   
  1930. Program 33. Confirms that the LEA instruction is the better 
  1931. choice for stack adjustments greater than 8 bytes.
  1932.  
  1933.  ; Program Name: LEA_ADDA.S
  1934.  ; Version 1.004
  1935.  
  1936.  ; Assembly Instructions:
  1937.  
  1938.  ;    Assemble in PC-relative mode and save with a TOS extension.
  1939.  
  1940.  ; Execution Instructions:
  1941.  
  1942.  ;     Execute from the desktop; or execute SPAWN.TTP, type LEA_ADDA.TOS on
  1943.  ; its command line and view this program's output in LEA_ADDA.DAT.
  1944.  
  1945.  ; Program Function:
  1946.  
  1947.  ;    Confirms (or refutes) the contention that lea $C(An), An is faster
  1948.  ; than adda.l #$C(An), where n = 1 - 7.  Reference: "68000 Tricks and Traps",
  1949.  ; by Mike Morton, p.166, Byte, September, 1986.  See the paragraph which
  1950.  ; begins "Small adjustments to the stack pointer...".
  1951.  
  1952. calculate_program_size:
  1953.  lea        -$102(pc), a1       ; Fetch basepage start address.
  1954.  lea        program_end, a0     ; Fetch program end address.
  1955.  trap       #6                  ; Return unused memory to op system.
  1956.  lea        stack, a7           ; Point A7 to this program's stack.
  1957.  
  1958. print_heading:
  1959.  lea        heading, a0
  1960.  bsr        print_string
  1961.  
  1962. lea_method:
  1963.  lea        lea_time_msg, a0    ; Print label for lea execution results.
  1964.  bsr        print_string
  1965.  move.l     #49999, d7          ; D7 is counter for the loop.
  1966.  trap       #3                  ; Value of system clock returned in D0.
  1967.  move.l     d0, d1              ; Save time in scratch register.
  1968.  
  1969. lea_loop:                       ; Marks start of instruction in the loop.
  1970.  lea        $C(a2), a2          ; Instruction in the loop.
  1971. memory_1:                       ; Marks end of instruction in the loop.
  1972.  dbra       d7, lea_loop        ; Loop 50000 times.
  1973.  trap       #3                  ; Get current value of system clock.
  1974.  sub.l      d1, d0              ; Subtract beginning value from ending value.
  1975.  mulu       #5, d0              ; Convert to milliseconds.
  1976.  sub.l      #80, d0             ; Subtract dbra time and "error".
  1977.                                 ; See Timing Note below.
  1978.  move.l     d0, d1              ; Transfer time for trap #4 call.
  1979. convert_lea_time_to_ASCII_decimal:
  1980.  trap       #4                  ; Address of decimal string returned in A0.
  1981. print_lea_time:
  1982.  bsr.s      print_string        ; Print the decimal string.
  1983.  lea        time_msg, a0        ; Print units label.
  1984.  bsr.s      print_string
  1985.  
  1986.  ; Timing Note:
  1987.  
  1988.  ;   Until the count has expired, each dbra instruction requires 10 clock
  1989.  ; periods to execute.  49,999 dbra instructions require that amount of
  1990.  ; time.  When the last dbra instruction is executed, the count has expired
  1991.  ; and that instruction requires 14 clock periods.
  1992.  
  1993.  ;   Total clock periods consumed by the dbra instruction is 
  1994.  
  1995.  ;                  49,999 X 10 + 14 = 500,004.
  1996.  
  1997.  ;   Each clock period is .000000125 second.  Total time consumed by the
  1998.  ; dbra instructions is
  1999.  
  2000.  ;        500,004 X .000000125 = .0625 second = 62.5 milliseconds.
  2001.  
  2002.  ;   If we assume that the 8 clock period execution time given in the
  2003.  ; Motorola reference manual for the lea d(An) instruction is correct,
  2004.  ; then 50,000 executions of lea $C(a2), a2 require 
  2005.  
  2006.  ;            50,000 X 8 X .000000125 = 50 milliseconds
  2007.  
  2008.  ;   Total time for the lea loop is 62.5 + 50 = 112.5 milliseconds.
  2009.  
  2010.  ;   When no adjustment is made for "overhead" time, such as attendant
  2011.  ;   instructions, which includes the dbra instruction, trap invocations and
  2012.  ;   a few others, this program reports a lea loop execution time of 130
  2013.  ;   milliseconds.  We can assume that the excess 17.5 milliseconds (130-112.5)
  2014.  ;   is partially due to the "overhead" time and partially due to a system
  2015.  ;   clock frequency different from 8 mhz.  We can simply combine both
  2016.  ;   components into "overhead" time.
  2017.  
  2018.  ;   With no adjustment for "overhead" time this program reports a loop
  2019.  ;   execution time of 180 milliseconds for the adda.l #$C, a2 loop.  We
  2020.  ;   subtract the 17.5 milliseconds "overhead" from this to obtain the true
  2021.  ;   loop time of 162.5 milliseconds.
  2022.  
  2023.  ;   From this we subtract the dbra execution time of 62.5 milliseconds to
  2024.  ;   get 100 milliseconds for 50,000 adda.l instruction..
  2025.  
  2026.  ;       62.5 milliseconds / 50,000 = .000002 second per instruction.
  2027.  
  2028.  ;         .000002 / .000000125 = 16 clock periods per instruction 
  2029.  
  2030.  ;   The execution time given in the Motorola manual for the adda.l #d, An 
  2031.  ;   instruction is 16 clock periods.
  2032.  
  2033.  ;   These calculations validate the method and justify subtracting the
  2034.  ;   "overhead" time from the total loop execution times before printing the
  2035.  ;   execution time for 50,000 executions of each of the two instructions of
  2036.  ;   interest.  Finally, we can combine dbra time and "overhead" time into
  2037.  ;   a "new" overhead time that is 17.5 + 62.5 = 80 milliseconds.
  2038.  
  2039.  ;   When the adjustments are made to the recorded loop times, the program
  2040.  ;   prints out the correct time for 50,000 executions of each instruction,
  2041.  ;   and the output correctly indicates that the lea instruction used in
  2042.  ;   the program is twice as fast as the adda.l instruction used.  The 
  2043.  ;   execution results of the program agree with the timing data given in
  2044.  ;   the Motorola reference guide.
  2045.  
  2046. adda_method:
  2047.  lea        adda_time_msg, a0   ; Print label for adda execution results.
  2048.  bsr.s      print_string
  2049.  move.l     #49999, d7          ; D7 is counter for the loop.
  2050.  trap       #3                  ; Value of system clock returned in D0.
  2051.  move.l     d0, d1              ; Save time in scratch register.
  2052.  
  2053. adda_loop:                      ; Marks start of instruction in the loop.   
  2054.  adda.l     #$C, a2             ; Instruction in the loop.
  2055. memory_2:                       ; Marks end of instruction in the loop.
  2056.  dbra       d7, adda_loop       ; Loop 50000 times.
  2057.  trap       #3                  ; Get current value of system clock.
  2058.  sub.l      d1, d0              ; Subtract beginning value from ending value.
  2059.  mulu       #5, d0              ; Convert to milliseconds.
  2060.  sub.l      #80, d0             ; Subtract dbra time and "error".
  2061.  move.l     d0, d1              ; Transfer time for trap #4 call.
  2062. convert_adda_time_to_ASCII_decimal:
  2063.  trap       #4                  ; Address of decimal string returned in A0.
  2064. print_adda_time:
  2065.  bsr.s      print_string        ; Print the decimal string.
  2066.  lea        time_msg, a0        ; Print units label.
  2067.  bsr.s      print_string
  2068.  
  2069. print_lea_requisite_memory:
  2070.  lea        lea_memory_msg, a0
  2071.  bsr.s      print_string
  2072.  lea        memory_1, a0        ; Calculate number of bytes occupied by the
  2073.  lea        lea_loop, a1        ; instruction in the loop.
  2074.  bsr.s      print_requisite_memory
  2075.  
  2076. print_adda_requisite_memory:
  2077.  lea        adda_memory_msg, a0
  2078.  bsr.s      print_string
  2079.  lea        memory_2, a0        ; Calculate number of bytes occupied by the
  2080.  lea        adda_loop, a1       ; instruction in the loop.
  2081.  bsr.s      print_requisite_memory
  2082.  
  2083. terminate:
  2084.  trap       #8
  2085.  
  2086.  ; SUBROUTINES
  2087.  
  2088. print_requisite_memory:
  2089.  suba.l     a1, a0
  2090.  move.l     a0, d1
  2091.  trap       #4                  ; Returns address of decimal string in A0.
  2092.  bsr.s      print_string
  2093.  lea        memory_msg, a0
  2094.  bsr.s      print_string
  2095.  rts
  2096.  
  2097. print_string:                   ; Expects address of string to be in A0.
  2098.  pea        (a0)                ; Push address of string onto stack.
  2099.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  2100.  trap       #1                  ; GEMDOS call
  2101.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  2102.  rts
  2103.  
  2104.  data
  2105. heading:         dc.b $D,$A,'LEA_ADDA Execution Results',$D,$A,$D,$A,0
  2106. lea_time_msg:    dc.b '   Time for 50,000 lea instructions:     ',0
  2107. adda_time_msg:   dc.b '   Time for 50,000 adda.l instructions: ',0
  2108. time_msg:        dc.b ' milliseconds',$D,$A,0
  2109. lea_memory_msg:  dc.b $D,$A,'   LEA requisite memory:    ',0
  2110. adda_memory_msg: dc.b       '   ADDA.L requisite memory: ',0
  2111. memory_msg:      dc.b ' bytes',$D,$A,0
  2112.  bss
  2113.  align                          ; Align storage on a word boundary.
  2114.                  ds.l     96
  2115. stack:           ds.l      0
  2116. program_end:     ds.l      0    ; Marks the end of program memory.
  2117.  end
  2118.  
  2119. LEA_ADDA Execution Results
  2120.  
  2121.    Time for 50,000 lea instructions:      50 milliseconds
  2122.    Time for 50,000 adda.l instructions:  100 milliseconds
  2123.  
  2124.    LEA requisite memory:     4 bytes
  2125.    ADDA.L requisite memory:  6 bytes
  2126.   
  2127.  
  2128.      The programs that I have used to compare the execution 
  2129. speeds and requisite memory of specific instructions and 
  2130. algorithms should have, by now, revealed their similarity of 
  2131. structure.  It is appropriate that I present a more general 
  2132. algorithm which can be used to perform such comparisons.  By 
  2133. endowing this algorithm with a certain amount of 
  2134. intelligence, I will be able to reduce the size of the 
  2135. programs that compare algorithms, and, in addition, I will 
  2136. be able to introduce the subject of algorithmic 
  2137. intelligence, itself.
  2138.      At first, my inclination was to include the general 
  2139. purpose performance testing algorithm in this chapter, but 
  2140. the detail with which I intend to discuss it and its 
  2141. ramifications would force this chapter to become very long 
  2142. and unwieldy, therefore, I have decided to devote an 
  2143. exclusive chapter to the algorithm.  The subject of the next 
  2144. chapter, then, is the design of an algorithm that is smart 
  2145. enough to accept a variety of instructions or other 
  2146. algorithms as input and to generate suitable data for 
  2147. execution speed and requisite memory comparisons.
  2148.      I suggest that, if at all possible, you read The Micro 
  2149. Millennium, by Christopher Evans, first published in 1980 by 
  2150. The Viking Press, before you turn to chapter 7.  At the very 
  2151. least, I think that you should read Chapters 12 through 14 
  2152. which discuss the following subjects: The Nature of 
  2153. Intelligence, Can a Machine Think? and Towards the Ultra 
  2154. Intelligent Machine.  If you follow this advice, then you 
  2155. will probably feel more comfortable with my use of the word 
  2156. intelligence as it applies to computers and computer 
  2157. algorithms.
  2158.  
  2159. Conclusion
  2160.   
  2161.      The emphasis in this chapter, as it has been in 
  2162. previous chapters and as it will continue to be in future 
  2163. chapters, is accuracy, reliability, execution speed and 
  2164. minimum requisite memory.  I feel that it is via a constant 
  2165. pressure to maintain these algorithmic attributes that all 
  2166. computer programming goals are eventually achieved.  In 
  2167. chapter 7, I shall illustrate the manner in which the desire 
  2168. to maintain these attributes leads to the development of 
  2169. algorithms which assume an intelligence of their own, 
  2170. thereby violating the computers are dumb principle.
  2171.  
  2172.